Compare commits
600 Commits
dev-export
...
wip-swaync
Author | SHA1 | Date | |
---|---|---|---|
3c5fee5d14 | |||
23c46079a9 | |||
df9ffcb7b1 | |||
f4f1917ed6 | |||
851b2cec88 | |||
28d4a4b065 | |||
7c5f5bd604 | |||
7e4899832d | |||
226c4ba818 | |||
76b6b71879 | |||
4951520584 | |||
e30d452254 | |||
18a7598f62 | |||
4d3e482174 | |||
68556222e2 | |||
2275fc20cd | |||
7c247a6d39 | |||
1483dac941 | |||
e1a8c94ab9 | |||
b0e66056ec | |||
08dd4ca641 | |||
f6eadd3696 | |||
b59685cc9d | |||
c30e131aa7 | |||
5adf6c0194 | |||
6b7507384c | |||
e97d844380 | |||
0628bd7880 | |||
bee3b664c9 | |||
15cade99e7 | |||
4150fab10b | |||
25e314c02e | |||
ed0528fafa | |||
c5ad11a243 | |||
68de71084b | |||
713bbffd7d | |||
028689cf86 | |||
5d34139da6 | |||
626fe1946d | |||
6d8f9edfd0 | |||
745362e05e | |||
000bae364e | |||
3667484e80 | |||
c459eb0118 | |||
1c483992da | |||
55680b68b2 | |||
b3f5bf4e80 | |||
51995a7d95 | |||
462f9d3ab3 | |||
fd00eaede8 | |||
85421f82c1 | |||
e86d6934fd | |||
db028dcfe2 | |||
ad2fef5b48 | |||
66524685a9 | |||
1d7c54b20e | |||
d68cc761cc | |||
25c13705cd | |||
55e2aaf3a1 | |||
94f2096219 | |||
8f5f3933c1 | |||
e2d72f9e54 | |||
fc84aa88ee | |||
841fb4bf7a | |||
5f789b3db2 | |||
49fbe5f4fa | |||
ec4b974f3d | |||
84ad85a81e | |||
1af7450610 | |||
21912f0c4f | |||
05513da298 | |||
30486f4b4e | |||
974ca87983 | |||
bb217ecd7b | |||
ed92fafdf6 | |||
228fd2353a | |||
69ac75131c | |||
275f1ba49f | |||
501e79006c | |||
1ced3db806 | |||
d1513b5816 | |||
a225b7e5f5 | |||
e7768572e5 | |||
a26a398181 | |||
c59e9b09fc | |||
81c8af54a1 | |||
2d9ac4ca1e | |||
a9f56d9216 | |||
fb33ac6d1b | |||
eaed914c8b | |||
b10425f6b6 | |||
7541d5466e | |||
644084f176 | |||
baca7931ad | |||
2ee7af064d | |||
e1a80d6752 | |||
523e859ee4 | |||
230ca20017 | |||
30529182b0 | |||
2947e6635d | |||
3e1e7d49f8 | |||
4894a68c62 | |||
bd2775ded2 | |||
88ea557cd5 | |||
3e8ad5b899 | |||
fafe7242f7 | |||
1a01a40e85 | |||
f2f721234d | |||
ea19eac1c9 | |||
ed1d4398a1 | |||
12e106ee2a | |||
d13007fc12 | |||
2fa00b4c73 | |||
c1e17a0693 | |||
cd617cc034 | |||
5607bae49b | |||
f70c467971 | |||
6cb5edbfff | |||
5a844762c2 | |||
de9b1e6197 | |||
f43bb446c8 | |||
fa8e014eae | |||
6191542805 | |||
b8f13cd965 | |||
ee2b1f245e | |||
f11f91b9fc | |||
296a48caf1 | |||
f58bfb3c42 | |||
cbaaa984b6 | |||
6e4f0af012 | |||
3942ae0f1b | |||
fa65b0b92e | |||
ca998dc2be | |||
b6a2107b1c | |||
697ae02797 | |||
ab35a46e5f | |||
a6179b8234 | |||
d90aa693f9 | |||
b23c3cbf61 | |||
55ad5dcc01 | |||
90b1215a89 | |||
a218ddb202 | |||
8dc7eff545 | |||
77b4e7ff09 | |||
827d9626d6 | |||
cdfcf1a46d | |||
e8c4555be7 | |||
0092ccacbe | |||
184e37e2dc | |||
8859b4cf8a | |||
5a2382f61c | |||
f6c56969bc | |||
1f0fad62a7 | |||
5b633d20bc | |||
a918aa0c2f | |||
93a265f34a | |||
b818972597 | |||
476b481fd7 | |||
631235e56b | |||
ea4063340d | |||
f2ad69af1f | |||
e34ca0fec9 | |||
43464e658f | |||
56070547b1 | |||
0593971917 | |||
b77650219a | |||
ad1ebc0ed3 | |||
ae64493564 | |||
1272b941c2 | |||
3d63c33669 | |||
fcbc558de9 | |||
b180adcf48 | |||
342c9bbbef | |||
233faaadac | |||
aaf9dbac1e | |||
b7d90c3b6d | |||
a4b54cd9c1 | |||
d6c5580fc3 | |||
7d63960e6f | |||
8dc1cbbbd2 | |||
6253995f6c | |||
835397ad29 | |||
042e6ae3f9 | |||
5b5cfc40a8 | |||
3cf636f681 | |||
a5281c7f98 | |||
30c7fd8b09 | |||
710e4cc066 | |||
8b4a0a916b | |||
5cfde63d5d | |||
1cf442dffd | |||
6cda5cf49b | |||
0a3e6b34c7 | |||
322ef2c333 | |||
6ff72c83ae | |||
dc40395136 | |||
28a2042664 | |||
7aa3f1f989 | |||
08c92151eb | |||
5a753583bf | |||
c3d0b6b486 | |||
ff89819940 | |||
9a69d8bd0d | |||
091e525846 | |||
6dd1d5759b | |||
f3162544f7 | |||
1bf829dcf0 | |||
760326b38b | |||
0293773e64 | |||
6b6a9504e4 | |||
2de947d96e | |||
c493fcfd7f | |||
85e5d30b0f | |||
330864c866 | |||
29dde0240b | |||
114df5efab | |||
e28e60769a | |||
bc8cf58b5a | |||
d740dbe049 | |||
0eb8244897 | |||
69fe55961f | |||
aa18af8635 | |||
d47ed3dec9 | |||
045b5f0294 | |||
45e5752a05 | |||
2b39cfb57e | |||
1ffaa232d8 | |||
a9ddfb2752 | |||
4682ca32e2 | |||
b8ae4a284d | |||
f3c60ad136 | |||
3c6c70ba9f | |||
c0feffef1e | |||
6e80d4dfdf | |||
1f73573fe3 | |||
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 |
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:
|
||||||
|
48
TODO.md
48
TODO.md
@@ -1,10 +1,7 @@
|
|||||||
## BUGS
|
## BUGS
|
||||||
- mpv UI is sometimes blank for audio/podcasts?
|
|
||||||
- i think it's when the audio file has no thumbnail?
|
|
||||||
- why i need to manually restart `wireguard-wg-ovpns` on servo periodically
|
- why i need to manually restart `wireguard-wg-ovpns` on servo periodically
|
||||||
- else DNS fails
|
- else DNS fails
|
||||||
- fix epiphany URL bar input on moby
|
- ringer (i.e. dino incoming call) doesn't prevent moby from sleeping
|
||||||
- sxmo: wvkbd: missing font for icons on the 3rd page
|
|
||||||
|
|
||||||
## REFACTORING:
|
## REFACTORING:
|
||||||
|
|
||||||
@@ -33,6 +30,8 @@
|
|||||||
|
|
||||||
## IMPROVEMENTS:
|
## IMPROVEMENTS:
|
||||||
### security/resilience
|
### security/resilience
|
||||||
|
- matrix/ntfy: automatically add the ntfy.uninsane.org push URL as part of synapse launch
|
||||||
|
- ntfy: use a more secure topic
|
||||||
- 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
|
||||||
@@ -51,46 +50,57 @@
|
|||||||
- integrate `nix check` into Gitea actions?
|
- integrate `nix check` into Gitea actions?
|
||||||
|
|
||||||
### user experience
|
### user experience
|
||||||
- moby: sxmo: fix youtube scripts (package youtube-cli)
|
#### 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
|
||||||
|
- extend width to 100% of portrait mode
|
||||||
- moby: tune GPS
|
- moby: tune GPS
|
||||||
- run only geoclue, and not gpsd, to save power?
|
- run only geoclue, and not gpsd, to save power?
|
||||||
- tune QGPS setting in eg25-control, for less jitter?
|
- tune QGPS setting in eg25-control, for less jitter?
|
||||||
- direct mepo to prefer gpsd, with fallback to geoclue, for better accuracy?
|
- direct mepo to prefer gpsd, with fallback to geoclue, for better accuracy?
|
||||||
- configure geoclue to do some smoothing?
|
- configure geoclue to do some smoothing?
|
||||||
- manually do smoothing, as some layer between mepo and geoclue/gpsd?
|
- 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)
|
||||||
|
- 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
|
||||||
- could change junk filter from "no DKIM success" to explicit "DKIM failed"
|
- could change junk filter from "no DKIM success" to explicit "DKIM failed"
|
||||||
- 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
|
|
||||||
|
|
||||||
### 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?
|
|
||||||
- trying to auto-mount servo?
|
|
||||||
- something to do with systemd services restarting/stalling
|
|
||||||
- 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
|
||||||
|
|
||||||
|
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)
|
||||||
|
]
|
67
flake.lock
generated
67
flake.lock
generated
@@ -21,59 +21,27 @@
|
|||||||
"mobile-nixos": {
|
"mobile-nixos": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1690059310,
|
"lastModified": 1694749521,
|
||||||
"narHash": "sha256-4zcoDp8wwZVfGSzXltC5x+eH4kDWC/eJpyQNgr7shAA=",
|
"narHash": "sha256-MiVokKlpcJmfoGuWAMeW1En7gZ5hk0rCQArYm6P9XCc=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "mobile-nixos",
|
"repo": "mobile-nixos",
|
||||||
"rev": "56fc9f9619f305f0865354975a98d22410eed127",
|
"rev": "d25d3b87e7f300d8066e31d792337d9cd7ecd23b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
|
"ref": "d25d3b87e7f300d8066e31d792337d9cd7ecd23b",
|
||||||
"repo": "mobile-nixos",
|
"repo": "mobile-nixos",
|
||||||
"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": 1693097136,
|
"lastModified": 1698544399,
|
||||||
"narHash": "sha256-fBZSMdBaoZ0INFbyZ5s0DOF7zDNcLsLxgkwdDh3l9Pc=",
|
"narHash": "sha256-vhRmPyEyoPkrXF2iykBsWHA05MIaOSmMRLMF7Hul6+s=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "9117c4e9dc117a6cd0319cca40f2349ed333669d",
|
"rev": "d87c5d8c41c9b3b39592563242f3a448b5cc4bc9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -85,11 +53,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unpatched": {
|
"nixpkgs-unpatched": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1693377291,
|
"lastModified": 1698611440,
|
||||||
"narHash": "sha256-vYGY9bnqEeIncNarDZYhm6KdLKgXMS+HA2mTRaWEc80=",
|
"narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "e7f38be3775bab9659575f192ece011c033655f0",
|
"rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -102,7 +70,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 +83,11 @@
|
|||||||
"nixpkgs-stable": "nixpkgs-stable"
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1693404499,
|
"lastModified": 1698548647,
|
||||||
"narHash": "sha256-cx/7yvM/AP+o/3wPJmA9W9F+WHemJk5t+Xcr+Qwkqhg=",
|
"narHash": "sha256-7c03OjBGqnwDW0FBaBc+NjfEBxMkza+dxZGJPyIzfFE=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "d9c5dc41c4b1f74c77f0dbffd0f3a4ebde447b7a",
|
"rev": "632c3161a6cc24142c8e3f5529f5d81042571165",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -152,11 +119,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1691106178,
|
"lastModified": 1698634059,
|
||||||
"narHash": "sha256-3mZ9gTvMpbZA9ea9ovoQpn2wKuQY0QZ7MDdEjArYdAQ=",
|
"narHash": "sha256-+Oyv6vDyCtBzab/5cTG0nUrHD9gj7KgGfD4D1Rn4fCk=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "f4d91aa201b6e49af690f250d4786bd1d8b4dcfd",
|
"rev": "2419750ca98fc04af42c91e50c49a29c68d465d2",
|
||||||
"revCount": 205,
|
"revCount": 210,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.uninsane.org/colin/uninsane"
|
"url": "https://git.uninsane.org/colin/uninsane"
|
||||||
},
|
},
|
||||||
|
172
flake.nix
172
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,26 @@
|
|||||||
|
|
||||||
mobile-nixos = {
|
mobile-nixos = {
|
||||||
# <https://github.com/nixos/mobile-nixos>
|
# <https://github.com/nixos/mobile-nixos>
|
||||||
url = "github:nixos/mobile-nixos";
|
# only used for building disk images, not relevant after deployment
|
||||||
|
# TODO: replace with something else. commit `0f3ac0bef1aea70254a3bae35e3cc2561623f4c1`
|
||||||
|
# replaces the imageBuilder with a "new implementation from celun" and wildly breaks my use.
|
||||||
|
# pinning to d25d3b... is equivalent to holding at 2023-09-15
|
||||||
|
url = "github:nixos/mobile-nixos?ref=d25d3b87e7f300d8066e31d792337d9cd7ecd23b";
|
||||||
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 +76,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}";
|
||||||
@@ -167,8 +187,8 @@
|
|||||||
imgs = mapAttrValues (host: host.config.system.build.img) self.nixosConfigurations;
|
imgs = mapAttrValues (host: host.config.system.build.img) self.nixosConfigurations;
|
||||||
|
|
||||||
# unofficial output
|
# unofficial output
|
||||||
host-pkgs = mapAttrValues (host: host.config.system.build.pkgs) self.nixosConfigurations;
|
hostPkgs = mapAttrValues (host: host.config.system.build.pkgs) self.nixosConfigurations;
|
||||||
host-programs = mapAttrValues (host: mapAttrValues (p: p.package) host.config.sane.programs) self.nixosConfigurations;
|
hostPrograms = mapAttrValues (host: mapAttrValues (p: p.package) host.config.sane.programs) self.nixosConfigurations;
|
||||||
|
|
||||||
overlays = {
|
overlays = {
|
||||||
# N.B.: `nix flake check` requires every overlay to take `final: prev:` at defn site,
|
# N.B.: `nix flake check` requires every overlay to take `final: prev:` at defn site,
|
||||||
@@ -184,17 +204,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 +256,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 +266,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 +327,31 @@
|
|||||||
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
|
||||||
|
|
||||||
|
specific build targets of interest:
|
||||||
|
- `nix build '.#imgs.rescue'`
|
||||||
'';
|
'';
|
||||||
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 = {
|
||||||
@@ -318,7 +391,23 @@
|
|||||||
'');
|
'');
|
||||||
};
|
};
|
||||||
|
|
||||||
check-nur = {
|
check = {
|
||||||
|
type = "app";
|
||||||
|
program = builtins.toString (pkgs.writeShellScript "check-all" ''
|
||||||
|
nix run '.#check.nur'
|
||||||
|
RC0=$?
|
||||||
|
nix run '.#check.host-configs'
|
||||||
|
RC1=$?
|
||||||
|
nix run '.#check.rescue'
|
||||||
|
RC2=$?
|
||||||
|
echo "nur: $RC0"
|
||||||
|
echo "host-configs: $RC1"
|
||||||
|
echo "rescue: $RC2"
|
||||||
|
exit $(($RC0 | $RC1 | $RC2))
|
||||||
|
'');
|
||||||
|
};
|
||||||
|
|
||||||
|
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";
|
||||||
@@ -330,7 +419,40 @@
|
|||||||
--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 -v '.#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))
|
||||||
|
''
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
check.rescue = {
|
||||||
|
type = "app";
|
||||||
|
program = builtins.toString (pkgs.writeShellScript "check-rescue" ''
|
||||||
|
nix build -v '.#imgs.rescue' --out-link ./result-rescue-img -j2
|
||||||
'');
|
'');
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -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-latest.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
|
||||||
|
@@ -31,28 +31,58 @@
|
|||||||
|
|
||||||
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.mercurial.enableFor.user.colin = false; # does not 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;
|
||||||
|
|
||||||
|
# enabled for easier debugging
|
||||||
sane.programs.eg25-control.enableFor.user.colin = true;
|
sane.programs.eg25-control.enableFor.user.colin = true;
|
||||||
|
sane.programs.rtl8723cs-wowlan.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.mpv.config.vo = "wlshm"; #< see hosts/common/programs/mpv.nix for details
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
# sane.programs.mpv.enableFor.user.colin = true;
|
# 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
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
boot.loader.efi.canTouchEfiVariables = false;
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
# /boot space is at a premium. default was 20.
|
# /boot space is at a premium. default was 20.
|
||||||
@@ -62,23 +92,24 @@
|
|||||||
# mobile.boot.stage-1.enable = false;
|
# mobile.boot.stage-1.enable = false;
|
||||||
# boot.initrd.systemd.enable = false;
|
# boot.initrd.systemd.enable = false;
|
||||||
# boot.initrd.services.swraid.enable = false; # attempt to fix dm_mod stuff
|
# boot.initrd.services.swraid.enable = false; # attempt to fix dm_mod stuff
|
||||||
# disable proximity sensor.
|
|
||||||
# the filtering/calibration is bad that it causes the screen to go fully dark at times.
|
|
||||||
boot.blacklistedKernelModules = [ "stk3310" ];
|
|
||||||
|
|
||||||
# without this some GUI apps fail: `DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory`
|
|
||||||
# this is because they can't allocate enough video ram.
|
|
||||||
# the default CMA seems to be 32M.
|
|
||||||
# i was running fine with 256MB from 2022/07-ish through 2022/12-ish, but then the phone quit reliably coming back from sleep: maybe a memory leak?
|
|
||||||
# `cat /proc/meminfo` to see CmaTotal/CmaFree if interested in tuning this.
|
|
||||||
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.override {
|
||||||
|
# rtl_bt = false probably means no bluetooth connectivity.
|
||||||
|
# N.B.: DON'T RE-ENABLE without first confirming that wake-on-lan works during suspend (rtcwake).
|
||||||
|
# it seems the rtl_bt stuff ("bluetooth coexist") might make wake-on-LAN radically more flaky.
|
||||||
|
rtl_bt = false;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
system.stateVersion = "21.11";
|
system.stateVersion = "21.11";
|
||||||
|
|
||||||
@@ -96,16 +127,17 @@
|
|||||||
# see pkgs/patched/alsa-ucm-conf for more info.
|
# see pkgs/patched/alsa-ucm-conf for more info.
|
||||||
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 = [
|
||||||
systemd =
|
(pkgs.alsa-ucm-conf-sane.override {
|
||||||
let ucm-env = config.environment.variables.ALSA_CONFIG_UCM2;
|
# internal speaker has a tendency to break :(
|
||||||
|
preferEarpiece = true;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
systemd = let
|
||||||
|
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;
|
||||||
@@ -114,6 +146,10 @@
|
|||||||
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...
|
# TODO: move elsewhere...
|
||||||
services.ModemManager.serviceConfig = {
|
services.ModemManager.serviceConfig = {
|
||||||
@@ -135,6 +171,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;
|
|
||||||
}
|
}
|
||||||
|
@@ -7,15 +7,38 @@
|
|||||||
# - `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
|
||||||
|
|
||||||
{ lib, ... }:
|
{ config, lib, ... }:
|
||||||
{
|
{
|
||||||
# test gpsd with `gpspipe -w -n 10 2> /dev/null | grep -m 1 TPV | jq '.lat, .lon' | tr '\n' ' '`
|
# test gpsd with `gpspipe -w -n 10 2> /dev/null | grep -m 1 TPV | jq '.lat, .lon' | tr '\n' ' '`
|
||||||
# ^ should return <lat> <long>
|
# ^ should return <lat> <long>
|
||||||
@@ -23,20 +46,24 @@
|
|||||||
services.gpsd.devices = [ "/dev/ttyUSB1" ];
|
services.gpsd.devices = [ "/dev/ttyUSB1" ];
|
||||||
|
|
||||||
# test geoclue2 by building `geoclue2-with-demo-agent`
|
# test geoclue2 by building `geoclue2-with-demo-agent`
|
||||||
# and running "${geoclue2-with-demo-agent}/libexec/geoclue-2/demos/where-am-i"
|
# 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.enable = true;
|
||||||
services.geoclue2.appConfig.where-am-i = {
|
services.geoclue2.appConfig.where-am-i = {
|
||||||
# this is the default "agent", shipped by geoclue package: allow it to use location
|
# this is the default "agent", shipped by geoclue package: allow it to use location
|
||||||
isAllowed = true;
|
isAllowed = true;
|
||||||
isSystem = false;
|
isSystem = false;
|
||||||
# XXX: setting users != [] might be causing `where-am-i` to time out
|
# XXX: setting users != [] might be causing `where-am-i` to time out
|
||||||
# users = [
|
users = [
|
||||||
# # restrict to only one set of users. empty array (default) means "allow any user to access geolocation".
|
# restrict to only one set of users. empty array (default) means "allow any user to access geolocation".
|
||||||
# (builtins.toString config.users.users.colin.uid)
|
(builtins.toString config.users.users.colin.uid)
|
||||||
# ];
|
];
|
||||||
};
|
};
|
||||||
systemd.services.geoclue.after = lib.mkForce []; #< defaults to network-online, but not all my sources require network
|
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.services.eg25-control.enable = true;
|
||||||
|
sane.programs.where-am-i.enableFor.user.colin = true;
|
||||||
}
|
}
|
||||||
|
@@ -66,6 +66,24 @@ in
|
|||||||
# target = "zImage"; # <-- confuses other parts of nixos :-(
|
# target = "zImage"; # <-- confuses other parts of nixos :-(
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# disable proximity sensor.
|
||||||
|
# the filtering/calibration is bad that it causes the screen to go fully dark at times.
|
||||||
|
boot.blacklistedKernelModules = [ "stk3310" ];
|
||||||
|
|
||||||
|
boot.kernelParams = [
|
||||||
|
# without this some GUI apps fail: `DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory`
|
||||||
|
# this is because they can't allocate enough video ram.
|
||||||
|
# see related nixpkgs issue: <https://github.com/NixOS/nixpkgs/issues/260222>
|
||||||
|
#
|
||||||
|
# the default CMA seems to be 32M.
|
||||||
|
# i was running fine with 256MB from 2022/07-ish through 2022/12-ish, but then the phone quit reliably coming back from sleep (phosh): maybe a memory leak?
|
||||||
|
# `cat /proc/meminfo` to see CmaTotal/CmaFree if interested in tuning this.
|
||||||
|
"cma=512M"
|
||||||
|
# 2023/10/20: potential fix for the lima (GPU) timeout bugs:
|
||||||
|
# - <https://gitlab.com/postmarketOS/pmaports/-/issues/805#note_890467824>
|
||||||
|
"lima.sched_timeout_ms=2000"
|
||||||
|
];
|
||||||
|
|
||||||
services.xserver.displayManager.job.preStart = ensureHWReady;
|
services.xserver.displayManager.job.preStart = ensureHWReady;
|
||||||
systemd.services.greetd.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;
|
||||||
@@ -35,94 +31,11 @@ in
|
|||||||
SXMO_LISGD_INPUT_DEVICE = "/dev/input/by-path/platform-1c2ac00.i2c-event";
|
SXMO_LISGD_INPUT_DEVICE = "/dev/input/by-path/platform-1c2ac00.i2c-event";
|
||||||
# vol and power are detected correctly by upstream
|
# vol and power are detected correctly by upstream
|
||||||
|
|
||||||
|
|
||||||
### preferences
|
### preferences
|
||||||
# notable bemenu options:
|
|
||||||
# - see `bemenu --help` for all
|
|
||||||
# -P, --prefix text to show before highlighted item.
|
|
||||||
# --scrollbar display scrollbar. (none (default), always, autohide)
|
|
||||||
# -H, --line-height defines the height to make each menu line (0 = default height). (wx)
|
|
||||||
# -M, --margin defines the empty space on either side of the menu. (wx)
|
|
||||||
# -W, --width-factor defines the relative width factor of the menu (from 0 to 1). (wx)
|
|
||||||
# -B, --border defines the width of the border in pixels around the menu. (wx)
|
|
||||||
# -R --border-radius defines the radius of the border around the menu (0 = no curved borders).
|
|
||||||
# --ch defines the height of the cursor (0 = scales with line height). (wx)
|
|
||||||
# --cw defines the width of the cursor. (wx)
|
|
||||||
# --hp defines the horizontal padding for the entries in single line mode. (wx)
|
|
||||||
# --fn defines the font to be used ('name [size]'). (wx)
|
|
||||||
# --tb defines the title background color. (wx)
|
|
||||||
# --tf defines the title foreground color. (wx)
|
|
||||||
# --fb defines the filter background color. (wx)
|
|
||||||
# --ff defines the filter foreground color. (wx)
|
|
||||||
# --nb defines the normal background color. (wx)
|
|
||||||
# --nf defines the normal foreground color. (wx)
|
|
||||||
# --hb defines the highlighted background color. (wx)
|
|
||||||
# --hf defines the highlighted foreground color. (wx)
|
|
||||||
# --fbb defines the feedback background color. (wx)
|
|
||||||
# --fbf defines the feedback foreground color. (wx)
|
|
||||||
# --sb defines the selected background color. (wx)
|
|
||||||
# --sf defines the selected foreground color. (wx)
|
|
||||||
# --ab defines the alternating background color. (wx)
|
|
||||||
# --af defines the alternating foreground color. (wx)
|
|
||||||
# --scb defines the scrollbar background color. (wx)
|
|
||||||
# --scf defines the scrollbar foreground color. (wx)
|
|
||||||
# --bdr defines the border color. (wx)
|
|
||||||
#
|
|
||||||
# colors are specified as `#RRGGBB`
|
|
||||||
# defaults:
|
|
||||||
# --ab "#222222"
|
|
||||||
# --af "#bbbbbb"
|
|
||||||
# --bdr "#005577"
|
|
||||||
# --border 3
|
|
||||||
# --cb "#222222"
|
|
||||||
# --center
|
|
||||||
# --cf "#bbbbbb"
|
|
||||||
# --fb "#222222"
|
|
||||||
# --fbb "#eeeeee"
|
|
||||||
# --fbf "#222222"
|
|
||||||
# --ff "#bbbbbb"
|
|
||||||
# --fixed-height
|
|
||||||
# --fn 'Sxmo 14'
|
|
||||||
# --hb "#005577"
|
|
||||||
# --hf "#eeeeee"
|
|
||||||
# --line-height 20
|
|
||||||
# --list 16
|
|
||||||
# --margin 40
|
|
||||||
# --nb "#222222"
|
|
||||||
# --nf "#bbbbbb"
|
|
||||||
# --no-overlap
|
|
||||||
# --no-spacing
|
|
||||||
# --sb "#323232"
|
|
||||||
# --scb "#005577"
|
|
||||||
# --scf "#eeeeee"
|
|
||||||
# --scrollbar autohide
|
|
||||||
# --tb "#005577"
|
|
||||||
# --tf "#eeeeee"
|
|
||||||
# --wrap
|
|
||||||
BEMENU_OPTS = let
|
|
||||||
bg = "#1d1721"; # slight purple
|
|
||||||
fg0 = "#d8d8d8"; # inactive text (light grey)
|
|
||||||
fg1 = "#ffffff"; # active text (white)
|
|
||||||
accent0 = "#1f5e54"; # darker but saturated teal
|
|
||||||
accent1 = "#418379"; # teal (matches nixos-bg)
|
|
||||||
accent2 = "#5b938a"; # brighter but muted teal
|
|
||||||
in lib.concatStringsSep " " [
|
|
||||||
"--wrap --scrollbar autohide --fixed-height"
|
|
||||||
"--center --margin 45"
|
|
||||||
"--no-spacing"
|
|
||||||
# XXX: font size doesn't seem to take effect (would prefer larger)
|
|
||||||
"--fn 'Sxmo 14' --line-height 22 --border 3"
|
|
||||||
"--bdr '${accent0}'" # border
|
|
||||||
"--scf '${accent2}' --scb '${accent0}'" # scrollbar
|
|
||||||
"--tb '${accent0}' --tf '${fg0}'" # title
|
|
||||||
"--fb '${accent0}' --ff '${fg1}'" # filter (i.e. text that's been entered)
|
|
||||||
"--hb '${accent1}' --hf '${fg1}'" # selected item
|
|
||||||
"--nb '${bg}' --nf '${fg0}'" # normal lines (even)
|
|
||||||
"--ab '${bg}' --af '${fg0}'" # alternated lines (odd)
|
|
||||||
"--cf '${accent0}' --cb '${accent0}'" # cursor (not very useful)
|
|
||||||
];
|
|
||||||
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 +47,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
|
||||||
@@ -144,9 +56,22 @@ in
|
|||||||
# SXMO_ROTATION_GRAVITY = "12500"; # kinda uncomfortable when walking
|
# SXMO_ROTATION_GRAVITY = "12500"; # kinda uncomfortable when walking
|
||||||
SXMO_ROTATION_GRAVITY = "12000";
|
SXMO_ROTATION_GRAVITY = "12000";
|
||||||
SXMO_SCREENSHOT_DIR = "/home/colin/Pictures"; # default: "$HOME"
|
SXMO_SCREENSHOT_DIR = "/home/colin/Pictures"; # default: "$HOME"
|
||||||
# test new scales by running `swaymsg -- output DSI-1 scale x.y`
|
|
||||||
|
# sway/wayland scaling:
|
||||||
|
# - conflicting info out there on how scaling actually works
|
||||||
|
# at the least, for things where it matters (mpv), it seems like scale settings have 0 effect on perf
|
||||||
|
# ways to enforce scaling:
|
||||||
|
# - <https://wiki.archlinux.org/title/HiDPI>
|
||||||
|
# - `swaymsg -- output DSI-1 scale 2.0` (scales everything)
|
||||||
|
# - `dconf write /org/gnome/desktop/interface/text-scaling-factor 2.0` (scales ONLY TEXT)
|
||||||
|
# - `GDK_DPI_SCALE=2.0` (scales ONLY TEXT)
|
||||||
|
#
|
||||||
|
# application notes:
|
||||||
|
# - cozy: in landscape, playback position is not visible unless scale <= 1.7
|
||||||
|
# - if in a tab, then scale 1.6 is the max
|
||||||
# SXMO_SWAY_SCALE = "1.5"; # hard to press gPodder icons
|
# SXMO_SWAY_SCALE = "1.5"; # hard to press gPodder icons
|
||||||
SXMO_SWAY_SCALE = "1.8";
|
SXMO_SWAY_SCALE = "1.6";
|
||||||
|
# SXMO_SWAY_SCALE = "1.8";
|
||||||
# SXMO_SWAY_SCALE = "2";
|
# SXMO_SWAY_SCALE = "2";
|
||||||
SXMO_WORKSPACE_WRAPPING = "5"; # how many workspaces. default: 4
|
SXMO_WORKSPACE_WRAPPING = "5"; # how many workspaces. default: 4
|
||||||
|
|
||||||
@@ -167,13 +92,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-latest.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
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -4,12 +4,15 @@
|
|||||||
./fs.nix
|
./fs.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
boot.loader.generic-extlinux-compatible.enable = true;
|
|
||||||
boot.loader.efi.canTouchEfiVariables = false;
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||||
# sane.persist.enable = false; # TODO: disable (but run `nix flake check` to ensure it works!)
|
sane.persist.enable = false;
|
||||||
sane.nixcache.enable = false; # don't want to be calling out to dead machines that we're *trying* to rescue
|
sane.nixcache.enable = false; # don't want to be calling out to dead machines that we're *trying* to rescue
|
||||||
|
|
||||||
|
# auto-login at shell
|
||||||
|
services.getty.autologinUser = "colin";
|
||||||
|
# users.users.colin.initialPassword = "colin";
|
||||||
|
|
||||||
# docs: https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion
|
# docs: https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion
|
||||||
system.stateVersion = "21.05";
|
system.stateVersion = "21.05";
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
|
@@ -36,10 +36,38 @@
|
|||||||
};
|
};
|
||||||
sane.fs."/mnt/persist/ext".mount = {};
|
sane.fs."/mnt/persist/ext".mount = {};
|
||||||
|
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
# 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 = [
|
||||||
{
|
{
|
||||||
|
@@ -1,6 +1,24 @@
|
|||||||
{ config, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
portOpts = with lib; types.submodule {
|
||||||
|
options = {
|
||||||
|
visibleTo.ovpn = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
{
|
{
|
||||||
|
options = with lib; {
|
||||||
|
sane.ports.ports = mkOption {
|
||||||
|
# add the `visibleTo.ovpn` option
|
||||||
|
type = types.attrsOf portOpts;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
networking.domain = "uninsane.org";
|
networking.domain = "uninsane.org";
|
||||||
|
|
||||||
sane.ports.openFirewall = true;
|
sane.ports.openFirewall = true;
|
||||||
@@ -83,6 +101,14 @@
|
|||||||
vpn-ip = "185.157.162.178";
|
vpn-ip = "185.157.162.178";
|
||||||
# DNS = 46.227.67.134, 192.165.9.158, 2a07:a880:4601:10f0:cd45::1, 2001:67c:750:1:cafe:cd45::1
|
# DNS = 46.227.67.134, 192.165.9.158, 2a07:a880:4601:10f0:cd45::1, 2001:67c:750:1:cafe:cd45::1
|
||||||
vpn-dns = "46.227.67.134";
|
vpn-dns = "46.227.67.134";
|
||||||
|
bridgePort = port: proto: ''
|
||||||
|
${in-ns} ${iptables} -A PREROUTING -t nat -p ${proto} --dport ${port} -m iprange --dst-range ${vpn-ip} \
|
||||||
|
-j DNAT --to-destination ${veth-host-ip}
|
||||||
|
'';
|
||||||
|
bridgeStatements = lib.foldlAttrs
|
||||||
|
(acc: port: portCfg: acc ++ (builtins.map (bridgePort port) portCfg.protocol))
|
||||||
|
[]
|
||||||
|
config.sane.ports.ports;
|
||||||
in {
|
in {
|
||||||
privateKeyFile = config.sops.secrets.wg_ovpns_privkey.path;
|
privateKeyFile = config.sops.secrets.wg_ovpns_privkey.path;
|
||||||
# wg is active only in this namespace.
|
# wg is active only in this namespace.
|
||||||
@@ -109,10 +135,10 @@
|
|||||||
# dynamicEndpointRefreshRestartSeconds = 5;
|
# dynamicEndpointRefreshRestartSeconds = 5;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
preSetup = "" + ''
|
preSetup = ''
|
||||||
${ip} netns add ovpns || echo "ovpns already exists"
|
${ip} netns add ovpns || echo "ovpns already exists"
|
||||||
'';
|
'';
|
||||||
postShutdown = "" + ''
|
postShutdown = ''
|
||||||
${in-ns} ip link del ovpns-veth-b || echo "couldn't delete ovpns-veth-b"
|
${in-ns} ip link del ovpns-veth-b || echo "couldn't delete ovpns-veth-b"
|
||||||
${ip} link del ovpns-veth-a || echo "couldn't delete ovpns-veth-a"
|
${ip} link del ovpns-veth-a || echo "couldn't delete ovpns-veth-a"
|
||||||
${ip} netns delete ovpns || echo "couldn't delete ovpns"
|
${ip} netns delete ovpns || echo "couldn't delete ovpns"
|
||||||
@@ -122,7 +148,7 @@
|
|||||||
${ip} rule add from all lookup local pref 0
|
${ip} rule add from all lookup local pref 0
|
||||||
${ip} rule del from all lookup local pref 100
|
${ip} rule del from all lookup local pref 100
|
||||||
'';
|
'';
|
||||||
postSetup = "" + ''
|
postSetup = ''
|
||||||
# DOCS:
|
# DOCS:
|
||||||
# - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/>
|
# - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/>
|
||||||
# - iptables primer: <https://danielmiessler.com/study/iptables/>
|
# - iptables primer: <https://danielmiessler.com/study/iptables/>
|
||||||
@@ -146,25 +172,11 @@
|
|||||||
${ip} rule add from all lookup local pref 100
|
${ip} rule add from all lookup local pref 100
|
||||||
${ip} rule del from all lookup local pref 0
|
${ip} rule del from all lookup local pref 0
|
||||||
|
|
||||||
# bridge HTTP traffic:
|
|
||||||
# any external port-80 request sent to the VPN addr will be forwarded to the rootns.
|
|
||||||
# this exists so LetsEncrypt can procure a cert for the MX over http.
|
|
||||||
# TODO: we could use _acme_challence.mx.uninsane.org CNAME to avoid this forwarding
|
|
||||||
# - <https://community.letsencrypt.org/t/where-does-letsencrypt-resolve-dns-from/37607/8>
|
|
||||||
${in-ns} ${iptables} -A PREROUTING -t nat -p tcp --dport 80 -m iprange --dst-range ${vpn-ip} \
|
|
||||||
-j DNAT --to-destination ${veth-host-ip}:80
|
|
||||||
|
|
||||||
# we also bridge DNS traffic
|
|
||||||
${in-ns} ${iptables} -A PREROUTING -t nat -p udp --dport 53 -m iprange --dst-range ${vpn-ip} \
|
|
||||||
-j DNAT --to-destination ${veth-host-ip}
|
|
||||||
${in-ns} ${iptables} -A PREROUTING -t nat -p tcp --dport 53 -m iprange --dst-range ${vpn-ip} \
|
|
||||||
-j DNAT --to-destination ${veth-host-ip}
|
|
||||||
|
|
||||||
# in order to access DNS in this netns, we need to route it to the VPN's nameservers
|
# in order to access DNS in this netns, we need to route it to the VPN's nameservers
|
||||||
# - alternatively, we could fix DNS servers like 1.1.1.1.
|
# - alternatively, we could fix DNS servers like 1.1.1.1.
|
||||||
${in-ns} ${iptables} -A OUTPUT -t nat -p udp --dport 53 -m iprange --dst-range 127.0.0.53 \
|
${in-ns} ${iptables} -A OUTPUT -t nat -p udp --dport 53 -m iprange --dst-range 127.0.0.53 \
|
||||||
-j DNAT --to-destination ${vpn-dns}:53
|
-j DNAT --to-destination ${vpn-dns}:53
|
||||||
'';
|
'' + (lib.concatStringsSep "\n" bridgeStatements);
|
||||||
};
|
};
|
||||||
|
|
||||||
# create a new routing table that we can use to proxy traffic out of the root namespace
|
# create a new routing table that we can use to proxy traffic out of the root namespace
|
||||||
@@ -217,4 +229,5 @@
|
|||||||
# # test with:
|
# # test with:
|
||||||
# # curl --interface hurricane http://[2607:f8b0:400a:80b::2004]
|
# # curl --interface hurricane http://[2607:f8b0:400a:80b::2004]
|
||||||
# # ping 2607:f8b0:400a:80b::2004
|
# # ping 2607:f8b0:400a:80b::2004
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,7 @@ in
|
|||||||
# > AttributeError: 'NoneType' object has no attribute 'query'
|
# > AttributeError: 'NoneType' object has no attribute 'query'
|
||||||
lib.mkIf false
|
lib.mkIf false
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
{ inherit user group; mode = "0700"; path = svc-dir; }
|
{ inherit user group; mode = "0700"; path = svc-dir; }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
124
hosts/by-name/servo/services/coturn.nix
Normal file
124
hosts/by-name/servo/services/coturn.nix
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
# TURN/STUN NAT traversal service
|
||||||
|
# commonly used to establish realtime calls with prosody, or possibly matrix/synapse
|
||||||
|
#
|
||||||
|
# - <https://github.com/coturn/coturn/>
|
||||||
|
# - `man turnserver`
|
||||||
|
# - config docs: <https://github.com/coturn/coturn/blob/master/examples/etc/turnserver.conf>
|
||||||
|
#
|
||||||
|
# N.B. during operation it's NORMAL to see "error 401".
|
||||||
|
# during session creation:
|
||||||
|
# - client sends Allocate request
|
||||||
|
# - server replies error 401, providing a realm and nonce
|
||||||
|
# - client uses realm + nonce + shared secret to construct an auth key & call Allocate again
|
||||||
|
# - server replies Allocate Success Response
|
||||||
|
# - source: <https://stackoverflow.com/a/66643135>
|
||||||
|
#
|
||||||
|
# N.B. this safest implementation routes all traffic THROUGH A VPN
|
||||||
|
# - that adds a lot of latency, but in practice turns out to be inconsequential.
|
||||||
|
# i guess ICE allows clients to prefer the other party's lower-latency server, in practice?
|
||||||
|
# - still, this is the "safe" implementation because STUN works with IP addresses instead of domain names:
|
||||||
|
# 1. client A queries the STUN server to determine its own IP address/port.
|
||||||
|
# 2. client A tells client B which IP address/port client A is visible on.
|
||||||
|
# 3. client B contacts that IP address/port
|
||||||
|
# this only works so long as the IP address/port which STUN server sees client A on is publicly routable.
|
||||||
|
# that is NOT the case when the STUN server and client A are on the same LAN
|
||||||
|
# even if client A contacts the STUN server via its WAN address with port reflection enabled.
|
||||||
|
# hence, there's no obvious way to put the STUN server on the same LAN as either client and expect the rest to work.
|
||||||
|
{ lib, ... }:
|
||||||
|
let
|
||||||
|
# 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.ports.ports = lib.mkMerge ([
|
||||||
|
{
|
||||||
|
"3478" = {
|
||||||
|
# this is the "control" port.
|
||||||
|
# i.e. no client data is forwarded through it, but it's where clients request tunnels.
|
||||||
|
protocol = [ "tcp" "udp" ];
|
||||||
|
# visibleTo.lan = true;
|
||||||
|
# visibleTo.wan = true;
|
||||||
|
visibleTo.ovpn = true;
|
||||||
|
description = "colin-stun-turn";
|
||||||
|
};
|
||||||
|
"5349" = {
|
||||||
|
# the other port 3478 also supports TLS/DTLS, but presumably clients wanting TLS will default 5349
|
||||||
|
protocol = [ "tcp" ];
|
||||||
|
# visibleTo.lan = true;
|
||||||
|
# visibleTo.wan = true;
|
||||||
|
visibleTo.ovpn = true;
|
||||||
|
description = "colin-stun-turn-over-tls";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
] ++ (builtins.map
|
||||||
|
(port: {
|
||||||
|
"${builtins.toString port}" = let
|
||||||
|
count = port - turnPortLow + 1;
|
||||||
|
numPorts = turnPortHigh - turnPortLow + 1;
|
||||||
|
in {
|
||||||
|
protocol = [ "tcp" "udp" ];
|
||||||
|
# visibleTo.lan = true;
|
||||||
|
# visibleTo.wan = true;
|
||||||
|
visibleTo.ovpn = true;
|
||||||
|
description = "colin-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
|
||||||
|
};
|
||||||
|
})
|
||||||
|
turnPortRange
|
||||||
|
));
|
||||||
|
|
||||||
|
services.nginx.virtualHosts."turn.uninsane.org" = {
|
||||||
|
# allow ACME to procure a cert via nginx for this domain
|
||||||
|
enableACME = true;
|
||||||
|
};
|
||||||
|
sane.dns.zones."uninsane.org".inet = {
|
||||||
|
# CNAME."turn" = "servo.wan";
|
||||||
|
# CNAME."turn" = "ovpns";
|
||||||
|
# CNAME."turn" = "native";
|
||||||
|
# XXX: SRV records have to point to something with a A/AAAA record; no CNAMEs
|
||||||
|
A."turn" = "%AOVPNS%";
|
||||||
|
# A."turn" = "%AWAN%";
|
||||||
|
|
||||||
|
SRV."_stun._udp" = "5 50 3478 turn";
|
||||||
|
SRV."_stun._tcp" = "5 50 3478 turn";
|
||||||
|
SRV."_stuns._tcp" = "5 50 5349 turn";
|
||||||
|
SRV."_turn._udp" = "5 50 3478 turn";
|
||||||
|
SRV."_turn._tcp" = "5 50 3478 turn";
|
||||||
|
SRV."_turns._tcp" = "5 50 5349 turn";
|
||||||
|
};
|
||||||
|
|
||||||
|
sane.derived-secrets."/var/lib/coturn/shared_secret.bin" = {
|
||||||
|
encoding = "base64";
|
||||||
|
# TODO: make this not globally readable
|
||||||
|
acl.mode = "0644";
|
||||||
|
};
|
||||||
|
sane.fs."/var/lib/coturn/shared_secret.bin".wantedBeforeBy = [ "coturn.service" ];
|
||||||
|
|
||||||
|
# provide access to certs
|
||||||
|
users.users.turnserver.extraGroups = [ "nginx" ];
|
||||||
|
|
||||||
|
services.coturn.enable = true;
|
||||||
|
services.coturn.realm = "turn.uninsane.org";
|
||||||
|
services.coturn.cert = "/var/lib/acme/turn.uninsane.org/fullchain.pem";
|
||||||
|
services.coturn.pkey = "/var/lib/acme/turn.uninsane.org/key.pem";
|
||||||
|
services.coturn.use-auth-secret = true;
|
||||||
|
services.coturn.static-auth-secret-file = "/var/lib/coturn/shared_secret.bin";
|
||||||
|
services.coturn.lt-cred-mech = true;
|
||||||
|
services.coturn.min-port = turnPortLow;
|
||||||
|
services.coturn.max-port = turnPortHigh;
|
||||||
|
# services.coturn.secure-stun = true;
|
||||||
|
services.coturn.extraConfig = lib.concatStringsSep "\n" [
|
||||||
|
"verbose"
|
||||||
|
# "Verbose" #< even MORE verbosity than "verbose"
|
||||||
|
# "no-multicast-peers" # disables sending to IPv4 broadcast addresses (e.g. 224.0.0.0/3)
|
||||||
|
"listening-ip=10.0.1.5"
|
||||||
|
# "external-ip=185.157.162.178/10.0.1.5"
|
||||||
|
"external-ip=185.157.162.178"
|
||||||
|
# "listening-ip=10.78.79.51" # can be specified multiple times; omit for *
|
||||||
|
# "external-ip=97.113.128.229/10.78.79.51"
|
||||||
|
# "external-ip=97.113.128.229"
|
||||||
|
# "mobility" # "mobility with ICE (MICE) specs support" (?)
|
||||||
|
];
|
||||||
|
}
|
@@ -2,6 +2,7 @@
|
|||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./calibre.nix
|
./calibre.nix
|
||||||
|
./coturn.nix
|
||||||
./ddns-afraid.nix
|
./ddns-afraid.nix
|
||||||
./ddns-he.nix
|
./ddns-he.nix
|
||||||
./email
|
./email
|
||||||
@@ -18,12 +19,13 @@
|
|||||||
./lemmy.nix
|
./lemmy.nix
|
||||||
./matrix
|
./matrix
|
||||||
./navidrome.nix
|
./navidrome.nix
|
||||||
./nixserve.nix
|
|
||||||
./nginx.nix
|
./nginx.nix
|
||||||
|
./nixserve.nix
|
||||||
|
./ntfy
|
||||||
./pict-rs.nix
|
./pict-rs.nix
|
||||||
./pleroma.nix
|
./pleroma.nix
|
||||||
./postgres.nix
|
./postgres.nix
|
||||||
./prosody.nix
|
./prosody
|
||||||
./transmission.nix
|
./transmission.nix
|
||||||
./trust-dns.nix
|
./trust-dns.nix
|
||||||
./wikipedia.nix
|
./wikipedia.nix
|
||||||
|
@@ -14,9 +14,25 @@
|
|||||||
#
|
#
|
||||||
# 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
|
let
|
||||||
# TODO: this range could be larger, but right now that's costly because each element is its own UPnP forward
|
# TODO: this range could be larger, but right now that's costly because each element is its own UPnP forward
|
||||||
# TURN port range (inclusive)
|
# TURN port range (inclusive)
|
||||||
@@ -24,8 +40,11 @@ let
|
|||||||
turnPortHigh = 49167;
|
turnPortHigh = 49167;
|
||||||
turnPortRange = lib.range turnPortLow turnPortHigh;
|
turnPortRange = lib.range turnPortLow turnPortHigh;
|
||||||
in
|
in
|
||||||
|
# XXX(2023/10/15): disabled in favor of Prosody.
|
||||||
|
# everything configured below was fine: used ejabberd for several months.
|
||||||
|
lib.mkIf false
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
{ user = "ejabberd"; group = "ejabberd"; path = "/var/lib/ejabberd"; }
|
{ user = "ejabberd"; group = "ejabberd"; path = "/var/lib/ejabberd"; }
|
||||||
];
|
];
|
||||||
sane.ports.ports = lib.mkMerge ([
|
sane.ports.ports = lib.mkMerge ([
|
||||||
@@ -98,6 +117,9 @@ in
|
|||||||
turnPortRange
|
turnPortRange
|
||||||
));
|
));
|
||||||
|
|
||||||
|
# this ejabberd config uses builtin STUN/TURN server, so hack to ensure no other implementation fights for ports
|
||||||
|
services.coturn.enable = false;
|
||||||
|
|
||||||
# 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.
|
||||||
# why is /var/lib/acme/* owned by `nginx` group??
|
# why is /var/lib/acme/* owned by `nginx` group??
|
||||||
@@ -163,284 +185,285 @@ in
|
|||||||
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 = ''
|
|
||||||
hosts:
|
|
||||||
- uninsane.org
|
|
||||||
|
|
||||||
# none | emergency | alert | critical | error | warning | notice | info | debug
|
# none | emergency | alert | critical | error | warning | notice | info | debug
|
||||||
loglevel: debug
|
loglevel = "debug";
|
||||||
# loglevel: info
|
acme.auto = false;
|
||||||
# loglevel: notice
|
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";
|
||||||
|
|
||||||
acme:
|
pam_userinfotype = "jid";
|
||||||
auto: false
|
acl = {
|
||||||
certfiles:
|
admin.user = [ "colin@uninsane.org" ];
|
||||||
- /var/lib/acme/uninsane.org/full.pem
|
local.user_regexp = "";
|
||||||
# ca_file: ${pkgs.cacert.unbundled}/etc/ssl/certs/
|
loopback.ip = [ "127.0.0.0/8" "::1/128" ];
|
||||||
# ca_file: ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
|
};
|
||||||
|
|
||||||
pam_userinfotype: jid
|
access_rules = {
|
||||||
|
local.allow = "local";
|
||||||
acl:
|
c2s_access.allow = "all";
|
||||||
admin:
|
announce.allow = "admin";
|
||||||
user:
|
configure.allow = "admin";
|
||||||
- "colin@uninsane.org"
|
muc_create.allow = "local";
|
||||||
local:
|
pubsub_createnode_access.allow = "all";
|
||||||
user_regexp: ""
|
trusted_network.allow = "loopback";
|
||||||
loopback:
|
};
|
||||||
ip:
|
|
||||||
- 127.0.0.0/8
|
|
||||||
- ::1/128
|
|
||||||
|
|
||||||
access_rules:
|
|
||||||
local:
|
|
||||||
allow: local
|
|
||||||
c2s_access:
|
|
||||||
allow: all
|
|
||||||
announce:
|
|
||||||
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>
|
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shaper-rules>
|
||||||
shaper_rules:
|
shaper_rules = {
|
||||||
# setting this to above 1 may break outgoing messages
|
# setting this to above 1 may break outgoing messages
|
||||||
# - maybe some servers rate limit? or just don't understand simultaneous connections?
|
# - maybe some servers rate limit? or just don't understand simultaneous connections?
|
||||||
max_s2s_connections: 1
|
max_s2s_connections = 1;
|
||||||
max_user_sessions: 10
|
max_user_sessions = 10;
|
||||||
max_user_offline_messages: 5000
|
max_user_offline_messages = 5000;
|
||||||
c2s_shaper:
|
c2s_shaper.fast = "all";
|
||||||
fast: all
|
s2s_shaper.med = "all";
|
||||||
s2s_shaper:
|
};
|
||||||
med: all
|
|
||||||
|
|
||||||
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shapers>
|
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shapers>
|
||||||
# this limits the bytes/sec.
|
# this limits the bytes/sec.
|
||||||
# for example, burst: 3_000_000 and rate: 100_000 means:
|
# for example, burst: 3_000_000 and rate: 100_000 means:
|
||||||
# - each client has a BW budget that accumulates 100kB/sec and is capped at 3 MB
|
# - each client has a BW budget that accumulates 100kB/sec and is capped at 3 MB
|
||||||
shaper:
|
shaper.fast = 1000000;
|
||||||
fast: 1000000
|
shaper.med = 500000;
|
||||||
med: 500000
|
# shaper.fast.rate = 1000000;
|
||||||
# fast:
|
# shaper.fast.burst_size = 10000000;
|
||||||
# - rate: 1000000
|
# shaper.med.rate = 500000;
|
||||||
# - burst_size: 10000000
|
# shaper.med.burst_size = 5000000;
|
||||||
# med:
|
|
||||||
# - rate: 500000
|
|
||||||
# - burst_size: 5000000
|
|
||||||
|
|
||||||
# see: <https://docs.ejabberd.im/admin/configuration/listen/>
|
# see: <https://docs.ejabberd.im/admin/configuration/listen/>
|
||||||
# s2s_use_starttls: true
|
# s2s_use_starttls = true;
|
||||||
s2s_use_starttls: optional
|
s2s_use_starttls = "optional";
|
||||||
# lessens 504: remote-server-timeout errors
|
# lessens 504: remote-server-timeout errors
|
||||||
# see: <https://github.com/processone/ejabberd/issues/3105#issuecomment-562182967>
|
# see: <https://github.com/processone/ejabberd/issues/3105#issuecomment-562182967>
|
||||||
negotiation_timeout: 60
|
negotiation_timeout = 60;
|
||||||
|
|
||||||
listen:
|
listen = [
|
||||||
-
|
{
|
||||||
port: 5222
|
port = 5222;
|
||||||
module: ejabberd_c2s
|
module = "ejabberd_c2s";
|
||||||
shaper: c2s_shaper
|
shaper = "c2s_shaper";
|
||||||
starttls: true
|
starttls = true;
|
||||||
access: c2s_access
|
access = "c2s_access";
|
||||||
-
|
}
|
||||||
port: 5223
|
{
|
||||||
module: ejabberd_c2s
|
port = 5223;
|
||||||
shaper: c2s_shaper
|
module = "ejabberd_c2s";
|
||||||
tls: true
|
shaper = "c2s_shaper";
|
||||||
access: c2s_access
|
tls = true;
|
||||||
-
|
access = "c2s_access";
|
||||||
port: 5269
|
}
|
||||||
module: ejabberd_s2s_in
|
{
|
||||||
shaper: s2s_shaper
|
port = 5269;
|
||||||
-
|
module = "ejabberd_s2s_in";
|
||||||
port: 5270
|
shaper = "s2s_shaper";
|
||||||
module: ejabberd_s2s_in
|
}
|
||||||
shaper: s2s_shaper
|
{
|
||||||
tls: true
|
port = 5270;
|
||||||
-
|
module = "ejabberd_s2s_in";
|
||||||
port: 5443
|
shaper = "s2s_shaper";
|
||||||
module: ejabberd_http
|
tls = true;
|
||||||
tls: true
|
}
|
||||||
request_handlers:
|
{
|
||||||
/admin: ejabberd_web_admin # TODO: ensure this actually works
|
port = 5443;
|
||||||
/api: mod_http_api # ejabberd API endpoint (to control server)
|
module = "ejabberd_http";
|
||||||
/bosh: mod_bosh
|
tls = true;
|
||||||
/upload: mod_http_upload
|
request_handlers = {
|
||||||
/ws: ejabberd_http_ws
|
"/admin" = "ejabberd_web_admin"; # TODO: ensure this actually works
|
||||||
# /.well-known/host-meta: mod_host_meta
|
"/api" = "mod_http_api"; # ejabberd API endpoint (to control server)
|
||||||
# /.well-known/host-meta.json: mod_host_meta
|
"/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
|
# STUN+TURN TCP
|
||||||
# note that the full port range should be forwarded ("not NAT'd")
|
# note that the full port range should be forwarded ("not NAT'd")
|
||||||
# `use_turn=true` enables both TURN *and* STUN
|
# `use_turn=true` enables both TURN *and* STUN
|
||||||
port: 3478
|
port = 3478;
|
||||||
module: ejabberd_stun
|
module = "ejabberd_stun";
|
||||||
transport: tcp
|
transport = "tcp";
|
||||||
use_turn: true
|
use_turn = true;
|
||||||
turn_min_port: ${builtins.toString turnPortLow}
|
turn_min_port = turnPortLow;
|
||||||
turn_max_port: ${builtins.toString turnPortHigh}
|
turn_max_port = turnPortHigh;
|
||||||
turn_ipv4_address: %ANATIVE%
|
turn_ipv4_address = "%ANATIVE%";
|
||||||
-
|
}
|
||||||
|
{
|
||||||
# STUN+TURN UDP
|
# STUN+TURN UDP
|
||||||
port: 3478
|
port = 3478;
|
||||||
module: ejabberd_stun
|
module = "ejabberd_stun";
|
||||||
transport: udp
|
transport = "udp";
|
||||||
use_turn: true
|
use_turn = true;
|
||||||
turn_min_port: ${builtins.toString turnPortLow}
|
turn_min_port = turnPortLow;
|
||||||
turn_max_port: ${builtins.toString turnPortHigh}
|
turn_max_port = turnPortHigh;
|
||||||
turn_ipv4_address: %ANATIVE%
|
turn_ipv4_address = "%ANATIVE%";
|
||||||
-
|
}
|
||||||
|
{
|
||||||
# STUN+TURN TLS over TCP
|
# STUN+TURN TLS over TCP
|
||||||
port: 5349
|
port = 5349;
|
||||||
module: ejabberd_stun
|
module = "ejabberd_stun";
|
||||||
transport: tcp
|
transport = "tcp";
|
||||||
tls: true
|
tls = true;
|
||||||
certfile: /var/lib/acme/uninsane.org/full.pem
|
certfile = "/var/lib/acme/uninsane.org/full.pem";
|
||||||
use_turn: true
|
use_turn = true;
|
||||||
turn_min_port: ${builtins.toString turnPortLow}
|
turn_min_port = turnPortLow;
|
||||||
turn_max_port: ${builtins.toString turnPortHigh}
|
turn_max_port = turnPortHigh;
|
||||||
turn_ipv4_address: %ANATIVE%
|
turn_ipv4_address = "%ANATIVE%";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
# TODO: enable mod_fail2ban
|
# TODO: enable mod_fail2ban
|
||||||
# TODO(low): look into mod_http_fileserver for serving macros?
|
# TODO(low): look into mod_http_fileserver for serving macros?
|
||||||
modules:
|
modules = {
|
||||||
# mod_adhoc: {}
|
# mod_adhoc = {};
|
||||||
# mod_announce:
|
# mod_announce = {
|
||||||
# access: admin
|
# access = "admin";
|
||||||
|
# };
|
||||||
# allows users to set avatars in vCard
|
# allows users to set avatars in vCard
|
||||||
# - <https://docs.ejabberd.im/admin/configuration/modules/#mod-avatar>
|
# - <https://docs.ejabberd.im/admin/configuration/modules/#mod-avatar>
|
||||||
mod_avatar: {}
|
mod_avatar = {};
|
||||||
mod_caps: {} # for mod_pubsub
|
mod_caps = {}; # for mod_pubsub
|
||||||
mod_carboncopy: {} # allows multiple clients to receive a user's message
|
mod_carboncopy = {}; # allows multiple clients to receive a user's message
|
||||||
# queues messages when recipient is offline, including PEP and presence messages.
|
# queues messages when recipient is offline, including PEP and presence messages.
|
||||||
# compliance test suggests this be enabled
|
# compliance test suggests this be enabled
|
||||||
mod_client_state: {}
|
mod_client_state = {};
|
||||||
|
|
||||||
# mod_conversejs: TODO: enable once on 21.12
|
# mod_conversejs: TODO: enable once on 21.12
|
||||||
# allows clients like Dino to discover where to upload files
|
# allows clients like Dino to discover where to upload files
|
||||||
mod_disco:
|
mod_disco.server_info = [
|
||||||
server_info:
|
{
|
||||||
-
|
modules = "all";
|
||||||
modules: all
|
name = "abuse-addresses";
|
||||||
name: abuse-addresses
|
urls = [
|
||||||
urls:
|
"mailto:admin.xmpp@uninsane.org"
|
||||||
- "mailto:admin.xmpp@uninsane.org"
|
"xmpp:colin@uninsane.org"
|
||||||
- "xmpp:colin@uninsane.org"
|
];
|
||||||
-
|
}
|
||||||
modules: all
|
{
|
||||||
name: admin-addresses
|
modules = "all";
|
||||||
urls:
|
name = "admin-addresses";
|
||||||
- "mailto:admin.xmpp@uninsane.org"
|
urls = [
|
||||||
- "xmpp:colin@uninsane.org"
|
"mailto:admin.xmpp@uninsane.org"
|
||||||
mod_http_upload:
|
"xmpp:colin@uninsane.org"
|
||||||
host: upload.xmpp.uninsane.org
|
];
|
||||||
hosts:
|
}
|
||||||
- upload.xmpp.uninsane.org
|
];
|
||||||
put_url: "https://@HOST@:5443/upload"
|
mod_http_upload = {
|
||||||
dir_mode: "0750"
|
host = "upload.xmpp.uninsane.org";
|
||||||
file_mode: "0750"
|
hosts = [ "upload.xmpp.uninsane.org" ];
|
||||||
rm_on_unregister: false
|
put_url = "https://@HOST@:5443/upload";
|
||||||
|
dir_mode = "0750";
|
||||||
|
file_mode = "0750";
|
||||||
|
rm_on_unregister = false;
|
||||||
|
};
|
||||||
# allow discoverability of BOSH and websocket endpoints
|
# allow discoverability of BOSH and websocket endpoints
|
||||||
# TODO: enable once on ejabberd 22.05 (presently 21.04)
|
# TODO: enable once on ejabberd 22.05 (presently 21.04)
|
||||||
# mod_host_meta: {}
|
# mod_host_meta = {};
|
||||||
mod_jidprep: {} # probably not needed: lets clients normalize jids
|
mod_jidprep = {}; # probably not needed: lets clients normalize jids
|
||||||
mod_last: {} # allow other users to know when i was last online
|
mod_last = {}; # allow other users to know when i was last online
|
||||||
mod_mam:
|
mod_mam = {
|
||||||
# Mnesia is limited to 2GB, better to use an SQL backend
|
# Mnesia is limited to 2GB, better to use an SQL backend
|
||||||
# For small servers SQLite is a good fit and is very easy
|
# For small servers SQLite is a good fit and is very easy
|
||||||
# to configure. Uncomment this when you have SQL configured:
|
# to configure. Uncomment this when you have SQL configured:
|
||||||
# db_type: sql
|
# db_type: sql
|
||||||
assume_mam_usage: true
|
assume_mam_usage = true;
|
||||||
default: always
|
default = "always";
|
||||||
mod_muc:
|
};
|
||||||
access:
|
mod_muc = {
|
||||||
- allow
|
access = [ "allow" ];
|
||||||
access_admin:
|
access_admin = { allow = "admin"; };
|
||||||
- allow: admin
|
access_create = "muc_create";
|
||||||
access_create: muc_create
|
access_persistent = "muc_create";
|
||||||
access_persistent: muc_create
|
access_mam = [ "allow" ];
|
||||||
access_mam:
|
history_size = 100; # messages to show new participants
|
||||||
- allow
|
host = "muc.xmpp.uninsane.org";
|
||||||
history_size: 100 # messages to show new participants
|
hosts = [ "muc.xmpp.uninsane.org" ];
|
||||||
host: muc.xmpp.uninsane.org
|
default_room_options = {
|
||||||
hosts:
|
anonymous = false;
|
||||||
- muc.xmpp.uninsane.org
|
lang = "en";
|
||||||
default_room_options:
|
persistent = true;
|
||||||
anonymous: false
|
mam = true;
|
||||||
lang: en
|
};
|
||||||
persistent: true
|
};
|
||||||
mam: true
|
mod_muc_admin = {};
|
||||||
mod_muc_admin: {}
|
mod_offline = {
|
||||||
mod_offline: # store messages for a user when they're offline (TODO: understand multi-client workflow?)
|
# store messages for a user when they're offline (TODO: understand multi-client workflow?)
|
||||||
access_max_user_messages: max_user_offline_messages
|
access_max_user_messages = "max_user_offline_messages";
|
||||||
store_groupchat: true
|
store_groupchat = true;
|
||||||
mod_ping: {}
|
};
|
||||||
mod_privacy: {} # deprecated, but required for `ejabberctl export_piefxis`
|
mod_ping = {};
|
||||||
mod_private: {} # allow local clients to persist arbitrary data on my server
|
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.
|
# 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
|
# 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.
|
# generally traverse 3rd party services. can opt to include message body, etc, though.
|
||||||
mod_push: {}
|
mod_push = {};
|
||||||
# i don't fully understand what this does, but it seems aimed at making push notifs more reliable.
|
# i don't fully understand what this does, but it seems aimed at making push notifs more reliable.
|
||||||
mod_push_keepalive: {}
|
mod_push_keepalive = {};
|
||||||
mod_roster:
|
mod_roster = {
|
||||||
versioning: true
|
versioning = true;
|
||||||
|
};
|
||||||
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-s2s-dialback>
|
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-s2s-dialback>
|
||||||
# s2s dialback to verify inbound messages
|
# s2s dialback to verify inbound messages
|
||||||
# unclear to what degree the XMPP network requires this
|
# unclear to what degree the XMPP network requires this
|
||||||
mod_s2s_dialback: {}
|
mod_s2s_dialback = {};
|
||||||
mod_shared_roster: {} # creates groups for @all, @online, and anything manually administered?
|
mod_shared_roster = {}; # creates groups for @all, @online, and anything manually administered?
|
||||||
mod_stream_mgmt:
|
mod_stream_mgmt = {
|
||||||
resend_on_timeout: if_offline # resend undelivered messages if the origin client is offline
|
# resend undelivered messages if the origin client is offline
|
||||||
|
resend_on_timeout = "if_offline";
|
||||||
|
};
|
||||||
# fallback for when DNS-based STUN discovery is unsupported.
|
# fallback for when DNS-based STUN discovery is unsupported.
|
||||||
# - see: <https://xmpp.org/extensions/xep-0215.html>
|
# - see: <https://xmpp.org/extensions/xep-0215.html>
|
||||||
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-stun-disco>
|
# 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?)
|
# people say to just keep this defaulted (i guess ejabberd knows to return its `host` option of uninsane.org?)
|
||||||
mod_stun_disco: {}
|
mod_stun_disco = {};
|
||||||
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-vcard>
|
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-vcard>
|
||||||
mod_vcard:
|
mod_vcard = {
|
||||||
allow_return_all: true # all users are discoverable (?)
|
allow_return_all = true; # all users are discoverable (?)
|
||||||
host: vjid.xmpp.uninsane.org
|
host = "vjid.xmpp.uninsane.org";
|
||||||
hosts:
|
hosts = [ "vjid.xmpp.uninsane.org" ];
|
||||||
- vjid.xmpp.uninsane.org
|
search = true;
|
||||||
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: {}
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
|
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,}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
@@ -22,6 +22,13 @@
|
|||||||
# - but postfix delegates authorization of that outgoing mail to dovecot, on the server side
|
# - but postfix delegates authorization of that outgoing mail to dovecot, on the server side
|
||||||
#
|
#
|
||||||
# - local clients (i.e. sendmail) interact only with postfix
|
# - local clients (i.e. sendmail) interact only with postfix
|
||||||
|
#
|
||||||
|
# debugging: general connectivity issues
|
||||||
|
# - test that inbound port 25 is unblocked:
|
||||||
|
# - `curl https://canyouseeme.org/ --data 'port=25&IP=185.157.162.178' | grep 'see your service'`
|
||||||
|
# - and retry with port 465, 587
|
||||||
|
# - i think this API requires the queried IP match the source IP
|
||||||
|
# - if necessary, `systemctl stop postfix` and `sudo nc -l 185.157.162.178 25`, then try https://canyouseeme.org
|
||||||
|
|
||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
|
@@ -18,7 +18,7 @@ let
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
# TODO: mode? could be more granular
|
# TODO: mode? could be more granular
|
||||||
{ user = "opendkim"; group = "opendkim"; path = "/var/lib/opendkim"; }
|
{ user = "opendkim"; group = "opendkim"; path = "/var/lib/opendkim"; }
|
||||||
{ user = "root"; group = "root"; path = "/var/lib/postfix"; }
|
{ user = "root"; group = "root"; path = "/var/lib/postfix"; }
|
||||||
@@ -28,21 +28,25 @@ in
|
|||||||
# "/var/lib/dovecot"
|
# "/var/lib/dovecot"
|
||||||
];
|
];
|
||||||
|
|
||||||
sane.ports.ports."25" = {
|
# XXX(2023/10/20): opening these ports in the firewall has the OPPOSITE effect as intended.
|
||||||
protocol = [ "tcp" ];
|
# these ports are only routable so long as they AREN'T opened.
|
||||||
visibleTo.ovpn = true;
|
# probably some cursed interaction with network namespaces introduced after 2023/10/10.
|
||||||
description = "colin-smtp-mx.uninsane.org";
|
# sane.ports.ports."25" = {
|
||||||
};
|
# protocol = [ "tcp" ];
|
||||||
sane.ports.ports."465" = {
|
# # XXX visibleTo.lan effectively means "open firewall, but don't configure any NAT/forwarding"
|
||||||
protocol = [ "tcp" ];
|
# visibleTo.lan = true;
|
||||||
visibleTo.ovpn = true;
|
# description = "colin-smtp-mx.uninsane.org";
|
||||||
description = "colin-smtps-mx.uninsane.org";
|
# };
|
||||||
};
|
# sane.ports.ports."465" = {
|
||||||
sane.ports.ports."587" = {
|
# protocol = [ "tcp" ];
|
||||||
protocol = [ "tcp" ];
|
# visibleTo.lan = true;
|
||||||
visibleTo.ovpn = true;
|
# description = "colin-smtps-mx.uninsane.org";
|
||||||
description = "colin-smtps-submission-mx.uninsane.org";
|
# };
|
||||||
};
|
# sane.ports.ports."587" = {
|
||||||
|
# protocol = [ "tcp" ];
|
||||||
|
# visibleTo.lan = true;
|
||||||
|
# description = "colin-smtps-submission-mx.uninsane.org";
|
||||||
|
# };
|
||||||
|
|
||||||
# exists only to manage certs for Postfix
|
# exists only to manage certs for Postfix
|
||||||
services.nginx.virtualHosts."mx.uninsane.org" = {
|
services.nginx.virtualHosts."mx.uninsane.org" = {
|
||||||
|
@@ -5,21 +5,33 @@
|
|||||||
./sftpgo.nix
|
./sftpgo.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
users.groups.export = {};
|
||||||
|
|
||||||
fileSystems."/var/export/media" = {
|
fileSystems."/var/export/media" = {
|
||||||
# everything in here could be considered publicly readable (based on the viewer's legal jurisdiction)
|
# everything in here could be considered publicly readable (based on the viewer's legal jurisdiction)
|
||||||
device = "/var/lib/uninsane/media";
|
device = "/var/lib/uninsane/media";
|
||||||
options = [ "rbind" ];
|
options = [ "rbind" ];
|
||||||
};
|
};
|
||||||
fileSystems."/var/export/playground" = {
|
# fileSystems."/var/export/playground" = {
|
||||||
device = config.fileSystems."/mnt/persist/ext".device;
|
# device = config.fileSystems."/mnt/persist/ext".device;
|
||||||
fsType = "btrfs";
|
# fsType = "btrfs";
|
||||||
options = [
|
# options = [
|
||||||
"subvol=export-playground"
|
# "subvol=export-playground"
|
||||||
"compress=zstd"
|
# "compress=zstd"
|
||||||
"defaults"
|
# "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" = {
|
sane.fs."/var/export/README.md" = {
|
||||||
wantedBy = [ "nfs.service" "sftpgo.service" ];
|
wantedBy = [ "nfs.service" "sftpgo.service" ];
|
||||||
@@ -29,14 +41,13 @@
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
# sane.fs."/var/lib/sftpgo/export/playground/README.md" = {
|
sane.fs."/var/export/playground/README.md" = {
|
||||||
# wantedBy = [ "nfs.service" "sftpgo.service" ];
|
wantedBy = [ "nfs.service" "sftpgo.service" ];
|
||||||
# file.text = ''
|
file.text = ''
|
||||||
# this directory is intentionally read+write by anyone.
|
this directory is intentionally read+write by anyone with access (i.e. on the LAN).
|
||||||
# there are no rules, except a server-level quota:
|
- share files
|
||||||
# - share files
|
- write poetry
|
||||||
# - write poetry
|
- be a friendly troll
|
||||||
# - be a friendly troll
|
'';
|
||||||
# '';
|
};
|
||||||
# };
|
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,20 @@
|
|||||||
# docs:
|
# docs:
|
||||||
# - <https://nixos.wiki/wiki/NFS>
|
# - <https://nixos.wiki/wiki/NFS>
|
||||||
# - <https://wiki.gentoo.org/wiki/Nfs-utils>
|
# - <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;
|
services.nfs.server.enable = true;
|
||||||
|
|
||||||
@@ -52,10 +64,47 @@
|
|||||||
# - no_root_squash, root_squash (default): map requests from uid 0 to user `nobody`.
|
# - no_root_squash, root_squash (default): map requests from uid 0 to user `nobody`.
|
||||||
# - crossmnt: reveal filesystems that are mounted under this endpoint
|
# - crossmnt: reveal filesystems that are mounted under this endpoint
|
||||||
# - fsid: must be zero for the root export
|
# - 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.
|
# - 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 (readonly) both to LAN (unencrypted) and wg vpn (encrypted)
|
# 10.0.0.0/8 to export both to LAN (readonly, unencrypted) and wg vpn (read-write, encrypted)
|
||||||
services.nfs.server.exports = ''
|
services.nfs.server.exports =
|
||||||
/var/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)
|
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;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@@ -9,9 +9,6 @@
|
|||||||
#
|
#
|
||||||
# TODO: change umask so sftpgo-created files default to 644.
|
# TODO: change umask so sftpgo-created files default to 644.
|
||||||
# - it does indeed appear that the 600 is not something sftpgo is explicitly doing.
|
# - it does indeed appear that the 600 is not something sftpgo is explicitly doing.
|
||||||
#
|
|
||||||
# TODO: enforce a "quota" by placing /playground on a btrfs subvolume
|
|
||||||
# - sane.persist API could expose a `subvolume` option to make this feel natural
|
|
||||||
|
|
||||||
|
|
||||||
{ config, lib, pkgs, sane-lib, ... }:
|
{ config, lib, pkgs, sane-lib, ... }:
|
||||||
@@ -126,6 +123,7 @@ in
|
|||||||
|
|
||||||
services.sftpgo = {
|
services.sftpgo = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
group = "export";
|
||||||
settings = {
|
settings = {
|
||||||
ftpd = {
|
ftpd = {
|
||||||
bindings = [
|
bindings = [
|
||||||
@@ -172,22 +170,15 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# fileSystems."/var/lib/sftpgo/export/media" = {
|
users.users.sftpgo.extraGroups = [ "export" ];
|
||||||
# # everything in here could be considered publicly readable (based on the viewer's legal jurisdiction)
|
|
||||||
# device = "/var/lib/uninsane/media";
|
systemd.services.sftpgo.serviceConfig = {
|
||||||
# options = [ "rbind" ];
|
ReadOnlyPaths = [ "/var/export" ];
|
||||||
# };
|
ReadWritePaths = [ "/var/export/playground" ];
|
||||||
# sane.persist.sys.plaintext = [
|
after = [ "network-online.target" ];
|
||||||
# { user = "sftpgo"; group = "sftpgo"; path = "/var/lib/sftpgo/export/playground"; }
|
wants = [ "network-online.target" ];
|
||||||
# ];
|
|
||||||
# sane.fs."/var/lib/sftpgo/export/playground/README.md" = {
|
Restart = "always";
|
||||||
# wantedBy = [ "sftpgo.service" ];
|
RestartSec = "20s";
|
||||||
# file.text = ''
|
};
|
||||||
# this directory is intentionally read+write by anyone.
|
|
||||||
# there are no rules, except a server-level quota:
|
|
||||||
# - share files
|
|
||||||
# - write poetry
|
|
||||||
# - be a friendly troll
|
|
||||||
# '';
|
|
||||||
# };
|
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
owner = config.users.users.freshrss.name;
|
owner = config.users.users.freshrss.name;
|
||||||
mode = "0400";
|
mode = "0400";
|
||||||
};
|
};
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
{ user = "freshrss"; group = "freshrss"; path = "/var/lib/freshrss"; }
|
{ user = "freshrss"; group = "freshrss"; path = "/var/lib/freshrss"; }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
# TODO: mode? could be more granular
|
# TODO: mode? could be more granular
|
||||||
{ user = "git"; group = "gitea"; path = "/var/lib/gitea"; }
|
{ user = "git"; group = "gitea"; path = "/var/lib/gitea"; }
|
||||||
];
|
];
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
lib.mkIf false # i don't actively use ipfs anymore
|
lib.mkIf false # i don't actively use ipfs anymore
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
# TODO: mode? could be more granular
|
# TODO: mode? could be more granular
|
||||||
{ user = "261"; group = "261"; path = "/var/lib/ipfs"; }
|
{ user = "261"; group = "261"; path = "/var/lib/ipfs"; }
|
||||||
];
|
];
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
# TODO: mode? we only need this to save Indexer creds ==> migrate to config?
|
# TODO: mode? we only need this to save Indexer creds ==> migrate to config?
|
||||||
{ user = "root"; group = "root"; path = "/var/lib/jackett"; }
|
{ user = "root"; group = "root"; path = "/var/lib/jackett"; }
|
||||||
];
|
];
|
||||||
|
@@ -40,7 +40,7 @@
|
|||||||
description = "colin-jellyfin-https-lan";
|
description = "colin-jellyfin-https-lan";
|
||||||
};
|
};
|
||||||
|
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin"; }
|
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin"; }
|
||||||
];
|
];
|
||||||
sane.fs."/var/lib/jellyfin/config/logging.json" = {
|
sane.fs."/var/lib/jellyfin/config/logging.json" = {
|
||||||
|
@@ -4,7 +4,7 @@ let
|
|||||||
inherit (svc-cfg) user group port stateDir;
|
inherit (svc-cfg) user group port stateDir;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
{ inherit user group; mode = "0700"; path = stateDir; }
|
{ inherit user group; mode = "0700"; path = stateDir; }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -1,6 +1,16 @@
|
|||||||
# docs: <https://nixos.wiki/wiki/Matrix>
|
# docs: <https://nixos.wiki/wiki/Matrix>
|
||||||
# docs: <https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-synapse>
|
# docs: <https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-synapse>
|
||||||
# example config: <https://github.com/matrix-org/synapse/blob/develop/docs/sample_config.yaml>
|
# example config: <https://github.com/matrix-org/synapse/blob/develop/docs/sample_config.yaml>
|
||||||
|
#
|
||||||
|
# ENABLING PUSH NOTIFICATIONS (with UnifiedPush/ntfy):
|
||||||
|
# - Matrix "pushers" API spec: <https://spec.matrix.org/latest/client-server-api/#post_matrixclientv3pushersset>
|
||||||
|
# - first, view notification settings:
|
||||||
|
# - obtain your client's auth token. e.g. Element -> profile -> help/about -> access token.
|
||||||
|
# - `curl --header 'Authorization: Bearer <your_access_token>' localhost:8008/_matrix/client/v3/pushers | jq .`
|
||||||
|
# - enable a new notification destination:
|
||||||
|
# - `curl --header "Authorization: Bearer <your_access_token>" --data '{ "app_display_name": "<topic>", "app_id": "ntfy.uninsane.org", "data": { "url": "https://ntfy.uninsane.org/_matrix/push/v1/notify", "format": "event_id_only" }, "device_display_name": "<topic>", "kind": "http", "lang": "en-US", "profile_tag": "", "pushkey": "<topic>" }' localhost:8008/_matrix/client/v3/pushers/set`
|
||||||
|
# - delete a notification destination by setting `kind` to `null` (otherwise, request is identical to above)
|
||||||
|
#
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -10,23 +20,24 @@
|
|||||||
./signal.nix
|
./signal.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
{ 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;
|
||||||
|
services.matrix-synapse.settings = {
|
||||||
# this changes the default log level from INFO to WARN.
|
# this changes the default log level from INFO to WARN.
|
||||||
# maybe there's an easier way?
|
# maybe there's an easier way?
|
||||||
services.matrix-synapse.settings.log_config = ./synapse-log_level.yaml;
|
log_config = ./synapse-log_level.yaml;
|
||||||
services.matrix-synapse.settings.server_name = "uninsane.org";
|
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" ];
|
||||||
@@ -42,35 +53,40 @@
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
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
|
||||||
];
|
];
|
||||||
|
|
||||||
# services.matrix-synapse.extraConfigFiles = [builtins.toFile "matrix-synapse-extra-config" ''
|
systemd.services.matrix-synapse.postStart = ''
|
||||||
# admin_contact: "admin.matrix@uninsane.org"
|
ACCESS_TOKEN=$(${pkgs.coreutils}/bin/cat ${config.sops.secrets.matrix_access_token.path})
|
||||||
# registrations_require_3pid:
|
TOPIC=$(${pkgs.coreutils}/bin/cat ${config.sops.secrets.ntfy-sh-topic.path})
|
||||||
# - email
|
|
||||||
# email:
|
echo "ensuring ntfy push gateway"
|
||||||
# smtp_host: "mx.uninsane.org"
|
${pkgs.curl}/bin/curl \
|
||||||
# smtp_port: 587
|
--header "Authorization: Bearer $ACCESS_TOKEN" \
|
||||||
# smtp_user: "matrix-synapse"
|
--data "{ \"app_display_name\": \"ntfy-adapter\", \"app_id\": \"ntfy.uninsane.org\", \"data\": { \"url\": \"https://ntfy.uninsane.org/_matrix/push/v1/notify\", \"format\": \"event_id_only\" }, \"device_display_name\": \"ntfy-adapter\", \"kind\": \"http\", \"lang\": \"en-US\", \"profile_tag\": \"\", \"pushkey\": \"$TOPIC\" }" \
|
||||||
# smtp_pass: "${secrets.matrix-synapse.smtp_pass}"
|
localhost:8008/_matrix/client/v3/pushers/set
|
||||||
# require_transport_security: true
|
|
||||||
# enable_tls: true
|
echo "registered push gateways:"
|
||||||
# notif_from: "%(app)s <notify.matrix@uninsane.org>"
|
${pkgs.curl}/bin/curl \
|
||||||
# app_name: "Uninsane Matrix"
|
--header "Authorization: Bearer $ACCESS_TOKEN" \
|
||||||
# enable_notifs: true
|
localhost:8008/_matrix/client/v3/pushers \
|
||||||
# validation_token_lifetime: 96h
|
| ${pkgs.jq}/bin/jq .
|
||||||
# invite_client_location: "https://web.matrix.uninsane.org"
|
'';
|
||||||
# subjects:
|
|
||||||
# email_validation: "[%(server_name)s] Validate your email"
|
|
||||||
# ''];
|
|
||||||
|
|
||||||
# new users may be registered on the CLI:
|
# new users may be registered on the CLI:
|
||||||
# register_new_matrix_user -c /nix/store/8n6kcka37jhmi4qpd2r03aj71pkyh21s-homeserver.yaml http://localhost:8008
|
# register_new_matrix_user -c /nix/store/8n6kcka37jhmi4qpd2r03aj71pkyh21s-homeserver.yaml http://localhost:8008
|
||||||
@@ -141,4 +157,9 @@
|
|||||||
sops.secrets."matrix_synapse_secrets.yaml" = {
|
sops.secrets."matrix_synapse_secrets.yaml" = {
|
||||||
owner = config.users.users.matrix-synapse.name;
|
owner = config.users.users.matrix-synapse.name;
|
||||||
};
|
};
|
||||||
|
sops.secrets."matrix_access_token" = {
|
||||||
|
owner = config.users.users.matrix-synapse.name;
|
||||||
|
};
|
||||||
|
# provide access to ntfy-sh-topic secret
|
||||||
|
users.users.matrix-synapse.extraGroups = [ "ntfy-sh" ];
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
# - recommended to use mautrix-discord: <https://github.com/NixOS/nixpkgs/pull/200462>
|
# - recommended to use mautrix-discord: <https://github.com/NixOS/nixpkgs/pull/200462>
|
||||||
lib.mkIf false
|
lib.mkIf false
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
{ user = "matrix-synapse"; group = "matrix-synapse"; path = "/var/lib/mx-puppet-discord"; }
|
{ user = "matrix-synapse"; group = "matrix-synapse"; path = "/var/lib/mx-puppet-discord"; }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -101,7 +101,7 @@ in
|
|||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
# TODO: mode?
|
# TODO: mode?
|
||||||
{ user = "matrix-appservice-irc"; group = "matrix-appservice-irc"; path = "/var/lib/matrix-appservice-irc"; }
|
{ user = "matrix-appservice-irc"; group = "matrix-appservice-irc"; path = "/var/lib/matrix-appservice-irc"; }
|
||||||
];
|
];
|
||||||
@@ -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";
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
# - <https://github.com/mautrix/signal/blob/master/mautrix_signal/example-config.yaml>
|
# - <https://github.com/mautrix/signal/blob/master/mautrix_signal/example-config.yaml>
|
||||||
{ config, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
{ user = "mautrix-signal"; group = "mautrix-signal"; path = "/var/lib/mautrix-signal"; }
|
{ user = "mautrix-signal"; group = "mautrix-signal"; path = "/var/lib/mautrix-signal"; }
|
||||||
{ user = "signald"; group = "signald"; path = "/var/lib/signald"; }
|
{ user = "signald"; group = "signald"; path = "/var/lib/signald"; }
|
||||||
];
|
];
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
{ user = "navidrome"; group = "navidrome"; path = "/var/lib/navidrome"; }
|
{ user = "navidrome"; group = "navidrome"; path = "/var/lib/navidrome"; }
|
||||||
];
|
];
|
||||||
services.navidrome.enable = true;
|
services.navidrome.enable = true;
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
# docs: https://nixos.wiki/wiki/Nginx
|
# docs: <https://nixos.wiki/wiki/Nginx>
|
||||||
|
# docs: <https://nginx.org/en/docs/>
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
@@ -133,7 +134,7 @@ in
|
|||||||
security.acme.acceptTerms = true;
|
security.acme.acceptTerms = true;
|
||||||
security.acme.defaults.email = "admin.acme@uninsane.org";
|
security.acme.defaults.email = "admin.acme@uninsane.org";
|
||||||
|
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
# TODO: mode?
|
# TODO: mode?
|
||||||
{ user = "acme"; group = "acme"; path = "/var/lib/acme"; }
|
{ user = "acme"; group = "acme"; path = "/var/lib/acme"; }
|
||||||
{ user = "colin"; group = "users"; path = "/var/www/sites"; }
|
{ user = "colin"; group = "users"; path = "/var/www/sites"; }
|
||||||
|
14
hosts/by-name/servo/services/ntfy/default.nix
Normal file
14
hosts/by-name/servo/services/ntfy/default.nix
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# ntfy: UnifiedPush notification delivery system
|
||||||
|
# - used to get push notifications out of Matrix and onto a Phone (iOS, Android, or a custom client)
|
||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./ntfy-waiter.nix
|
||||||
|
./ntfy-sh.nix
|
||||||
|
];
|
||||||
|
sops.secrets."ntfy-sh-topic" = {
|
||||||
|
mode = "0440";
|
||||||
|
owner = config.users.users.ntfy-sh.name;
|
||||||
|
group = config.users.users.ntfy-sh.name;
|
||||||
|
};
|
||||||
|
}
|
92
hosts/by-name/servo/services/ntfy/ntfy-sh.nix
Normal file
92
hosts/by-name/servo/services/ntfy/ntfy-sh.nix
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
# 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.byStore.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
|
||||||
|
'';
|
||||||
|
|
||||||
|
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
}
|
151
hosts/by-name/servo/services/ntfy/ntfy-waiter
Executable file
151
hosts/by-name/servo/services/ntfy/ntfy-waiter
Executable file
@@ -0,0 +1,151 @@
|
|||||||
|
#!/usr/bin/env nix-shell
|
||||||
|
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p ntfy-sh
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
LISTEN_QUEUE = 3
|
||||||
|
WAKE_MESSAGE = b'notification\n'
|
||||||
|
|
||||||
|
class Client:
|
||||||
|
def __init__(self, sock, addr_info, live_after: float):
|
||||||
|
self.live_after = live_after
|
||||||
|
self.sock = sock
|
||||||
|
self.addr_info = addr_info
|
||||||
|
|
||||||
|
def __cmp__(self, other: 'Client'):
|
||||||
|
return cmp(self.addr_info, other.addr_info)
|
||||||
|
|
||||||
|
def try_notify(self, message: bytes) -> bool:
|
||||||
|
"""
|
||||||
|
returns true if we send a packet to notify client.
|
||||||
|
fals otherwise (e.g. the socket is dead).
|
||||||
|
"""
|
||||||
|
ttl = self.live_after - time.time()
|
||||||
|
if ttl > 0:
|
||||||
|
logger.debug(f"sleeping {ttl:.2f}s until client {self.addr_info} is ready to receive notification")
|
||||||
|
time.sleep(ttl)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.sock.sendall(message)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"failed to notify client {self.addr_info} {e}")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
logger.info(f"successfully notified {self.addr_info}: {message}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
class Adapter:
|
||||||
|
def __init__(self, host: str, port: int, silence: int, topic: str):
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.silence = silence
|
||||||
|
self.topic = topic
|
||||||
|
self.clients = set()
|
||||||
|
|
||||||
|
def log_clients(self):
|
||||||
|
clients_str = '\n'.join(f' {c.addr_info}' for c in self.clients)
|
||||||
|
logger.debug(f"clients alive ({len(self.clients)}):\n{clients_str}")
|
||||||
|
|
||||||
|
def add_client(self, client: Client):
|
||||||
|
# it's a little bit risky to keep more than one client at the same IP address,
|
||||||
|
# because it's possible a notification comes in and we ring the old connection,
|
||||||
|
# even when the new connection says "don't ring yet".
|
||||||
|
for c in set(self.clients):
|
||||||
|
if c.addr_info[0] == client.addr_info[0]:
|
||||||
|
logger.info(f"purging old client before adding new one at same address: {c.addr_info} -> {client.addr_info}")
|
||||||
|
self.clients.remove(c)
|
||||||
|
|
||||||
|
logger.info(f"accepted client at {client.addr_info}")
|
||||||
|
self.clients.add(client)
|
||||||
|
|
||||||
|
def listener_loop(self):
|
||||||
|
logger.info(f"listening for connections on {self.host}:{self.port}")
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.bind((self.host, self.port))
|
||||||
|
s.listen(LISTEN_QUEUE)
|
||||||
|
while True:
|
||||||
|
conn, addr_info = s.accept()
|
||||||
|
self.add_client(Client(conn, addr_info, live_after = time.time() + self.silence))
|
||||||
|
|
||||||
|
def notify_clients(self, message: bytes = WAKE_MESSAGE):
|
||||||
|
# notify every client, and drop any which have disconnected.
|
||||||
|
# note that we notify based on age (oldest -> youngest)
|
||||||
|
# because notifying young clients might entail sleeping until they're ready.
|
||||||
|
clients = sorted(self.clients, key=lambda c: (c.live_after, c.addr_info))
|
||||||
|
|
||||||
|
dead_clients = [
|
||||||
|
c for c in clients if not c.try_notify(message)
|
||||||
|
]
|
||||||
|
for c in dead_clients:
|
||||||
|
self.clients.remove(c)
|
||||||
|
|
||||||
|
self.log_clients()
|
||||||
|
|
||||||
|
def notify_loop(self):
|
||||||
|
logger.info("waiting for notification events")
|
||||||
|
ntfy_proc = subprocess.Popen(
|
||||||
|
[
|
||||||
|
"ntfy",
|
||||||
|
"sub",
|
||||||
|
f"https://ntfy.uninsane.org/{self.topic}"
|
||||||
|
],
|
||||||
|
stdout=subprocess.PIPE
|
||||||
|
)
|
||||||
|
for line in iter(ntfy_proc.stdout.readline, b''):
|
||||||
|
logger.debug(f"received notification: {line}")
|
||||||
|
self.notify_clients()
|
||||||
|
|
||||||
|
def get_topic() -> str:
|
||||||
|
return open('/run/secrets/ntfy-sh-topic', 'rt').read().strip()
|
||||||
|
|
||||||
|
def run_forever(callable):
|
||||||
|
try:
|
||||||
|
callable()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"{callable} failed: {e}")
|
||||||
|
else:
|
||||||
|
logger.error(f"{callable} unexpectedly returned")
|
||||||
|
# sys.exit(1)
|
||||||
|
os._exit(1) # sometimes `sys.exit()` doesn't actually exit...
|
||||||
|
|
||||||
|
def main():
|
||||||
|
logging.basicConfig()
|
||||||
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="accept connections and notify the other end upon ntfy activity, with a guaranteed amount of silence")
|
||||||
|
parser.add_argument('--verbose', action='store_true')
|
||||||
|
parser.add_argument('--host', type=str, default='')
|
||||||
|
parser.add_argument('--port', type=int)
|
||||||
|
parser.add_argument('--silence', type=int, help="number of seconds to remain silent upon accepting a connection")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
|
else:
|
||||||
|
logging.getLogger().setLevel(logging.INFO)
|
||||||
|
|
||||||
|
adapter = Adapter(args.host, args.port, args.silence, get_topic())
|
||||||
|
|
||||||
|
listener_loop = threading.Thread(target=run_forever, name="listener_loop", args=(adapter.listener_loop,))
|
||||||
|
notify_loop = threading.Thread(target=run_forever, name="notify_loop", args=(adapter.notify_loop,))
|
||||||
|
|
||||||
|
# TODO: this method of exiting seems to sometimes leave the listener behind (?)
|
||||||
|
# preventing anyone else from re-binding the port.
|
||||||
|
listener_loop.start()
|
||||||
|
notify_loop.start()
|
||||||
|
listener_loop.join()
|
||||||
|
notify_loop.join()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
72
hosts/by-name/servo/services/ntfy/ntfy-waiter.nix
Normal file
72
hosts/by-name/servo/services/ntfy/ntfy-waiter.nix
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# service which adapts ntfy-sh into something suitable specifically for the Pinephone's
|
||||||
|
# wake-on-lan (WoL) feature.
|
||||||
|
# notably, it provides a mechanism by which the caller can be confident of an interval in which
|
||||||
|
# zero traffic will occur on the TCP connection, thus allowing it to enter sleep w/o fear of hitting
|
||||||
|
# race conditions in the Pinephone WoL feature.
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.sane.ntfy-waiter;
|
||||||
|
portLow = 5550;
|
||||||
|
portHigh = 5559;
|
||||||
|
portRange = lib.range portLow portHigh;
|
||||||
|
numPorts = portHigh - portLow + 1;
|
||||||
|
mkService = port: let
|
||||||
|
silence = port - portLow;
|
||||||
|
flags = lib.optional cfg.verbose "--verbose";
|
||||||
|
cli = [
|
||||||
|
"${cfg.package}/bin/ntfy-waiter"
|
||||||
|
"--port"
|
||||||
|
"${builtins.toString port}"
|
||||||
|
"--silence"
|
||||||
|
"${builtins.toString silence}"
|
||||||
|
] ++ flags;
|
||||||
|
in {
|
||||||
|
"ntfy-waiter-${builtins.toString silence}" = {
|
||||||
|
# TODO: run not as root (e.g. as ntfy-sh)
|
||||||
|
description = "wait for notification, with ${builtins.toString silence} seconds of guaranteed silence";
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "simple";
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = "5s";
|
||||||
|
ExecStart = lib.concatStringsSep " " cli;
|
||||||
|
};
|
||||||
|
after = [ "network.target" ];
|
||||||
|
wantedBy = [ "default.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = with lib; {
|
||||||
|
sane.ntfy-waiter.enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
sane.ntfy-waiter.verbose = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
sane.ntfy-waiter.package = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
default = pkgs.static-nix-shell.mkPython3Bin {
|
||||||
|
pname = "ntfy-waiter";
|
||||||
|
src = ./.;
|
||||||
|
pkgs = [ "ntfy-sh" ];
|
||||||
|
};
|
||||||
|
description = ''
|
||||||
|
exposed to provide an attr-path by which one may build the package for manual testing.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
sane.ports.ports = lib.mkMerge (lib.forEach portRange (port: {
|
||||||
|
"${builtins.toString port}" = {
|
||||||
|
protocol = [ "tcp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
|
description = "colin-notification-waiter-${builtins.toString (port+1)}-of-${builtins.toString numPorts}";
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
systemd.services = lib.mkMerge (builtins.map mkService portRange);
|
||||||
|
};
|
||||||
|
}
|
@@ -5,7 +5,7 @@ let
|
|||||||
cfg = config.services.pict-rs;
|
cfg = config.services.pict-rs;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = lib.mkIf cfg.enable [
|
sane.persist.sys.byStore.plaintext = lib.mkIf cfg.enable [
|
||||||
{ user = "pict-rs"; group = "pict-rs"; path = cfg.dataDir; }
|
{ user = "pict-rs"; group = "pict-rs"; path = cfg.dataDir; }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -14,7 +14,7 @@ let
|
|||||||
# logLevel = "debug";
|
# logLevel = "debug";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
{ user = "pleroma"; group = "pleroma"; path = "/var/lib/pleroma"; }
|
{ user = "pleroma"; group = "pleroma"; path = "/var/lib/pleroma"; }
|
||||||
];
|
];
|
||||||
services.pleroma.enable = true;
|
services.pleroma.enable = true;
|
||||||
|
@@ -6,7 +6,7 @@ let
|
|||||||
KiB = n: 1024*n;
|
KiB = n: 1024*n;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
# TODO: mode?
|
# TODO: mode?
|
||||||
{ user = "postgres"; group = "postgres"; path = "/var/lib/postgresql"; }
|
{ user = "postgres"; group = "postgres"; path = "/var/lib/postgresql"; }
|
||||||
];
|
];
|
||||||
|
@@ -1,81 +0,0 @@
|
|||||||
# example configs:
|
|
||||||
# - <https://github.com/kittywitch/nixfiles/blob/main/services/prosody.nix>
|
|
||||||
# create users with:
|
|
||||||
# - `sudo -u prosody prosodyctl adduser colin@uninsane.org`
|
|
||||||
|
|
||||||
{ lib, ... }:
|
|
||||||
|
|
||||||
# XXX disabled: doesn't send messages to nixnet.social (only receives them).
|
|
||||||
# nixnet runs ejabberd, so revisiting that.
|
|
||||||
lib.mkIf false
|
|
||||||
{
|
|
||||||
sane.persist.sys.plaintext = [
|
|
||||||
{ user = "prosody"; group = "prosody"; path = "/var/lib/prosody"; }
|
|
||||||
];
|
|
||||||
sane.ports.ports."5222" = {
|
|
||||||
protocol = [ "tcp" ];
|
|
||||||
visibleTo.lan = true;
|
|
||||||
visibleTo.wan = true;
|
|
||||||
description = "colin-xmpp-client-to-server";
|
|
||||||
};
|
|
||||||
sane.ports.ports."5269" = {
|
|
||||||
protocol = [ "tcp" ];
|
|
||||||
visibleTo.wan = true;
|
|
||||||
description = "colin-xmpp-server-to-server";
|
|
||||||
};
|
|
||||||
sane.ports.ports."5280" = {
|
|
||||||
protocol = [ "tcp" ];
|
|
||||||
visibleTo.lan = true;
|
|
||||||
visibleTo.wan = true;
|
|
||||||
description = "colin-xmpp-bosh";
|
|
||||||
};
|
|
||||||
sane.ports.ports."5281" = {
|
|
||||||
protocol = [ "tcp" ];
|
|
||||||
visibleTo.lan = true;
|
|
||||||
visibleTo.wan = true;
|
|
||||||
description = "colin-xmpp-prosody-https"; # necessary?
|
|
||||||
};
|
|
||||||
|
|
||||||
# provide access to certs
|
|
||||||
users.users.prosody.extraGroups = [ "nginx" ];
|
|
||||||
|
|
||||||
security.acme.certs."uninsane.org".extraDomainNames = [
|
|
||||||
"conference.xmpp.uninsane.org"
|
|
||||||
"upload.xmpp.uninsane.org"
|
|
||||||
];
|
|
||||||
|
|
||||||
services.prosody = {
|
|
||||||
enable = true;
|
|
||||||
admins = [ "colin@uninsane.org" ];
|
|
||||||
# allowRegistration = false;
|
|
||||||
# extraConfig = ''
|
|
||||||
# s2s_require_encryption = true
|
|
||||||
# c2s_require_encryption = true
|
|
||||||
# '';
|
|
||||||
|
|
||||||
extraModules = [ "private" "vcard" "privacy" "compression" "component" "muc" "pep" "adhoc" "lastactivity" "admin_adhoc" "blocklist"];
|
|
||||||
|
|
||||||
ssl.cert = "/var/lib/acme/uninsane.org/fullchain.pem";
|
|
||||||
ssl.key = "/var/lib/acme/uninsane.org/key.pem";
|
|
||||||
|
|
||||||
muc = [
|
|
||||||
{
|
|
||||||
domain = "conference.xmpp.uninsane.org";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
uploadHttp.domain = "upload.xmpp.uninsane.org";
|
|
||||||
|
|
||||||
virtualHosts = {
|
|
||||||
localhost = {
|
|
||||||
domain = "localhost";
|
|
||||||
enabled = true;
|
|
||||||
};
|
|
||||||
"xmpp.uninsane.org" = {
|
|
||||||
domain = "uninsane.org";
|
|
||||||
enabled = true;
|
|
||||||
ssl.cert = "/var/lib/acme/uninsane.org/fullchain.pem";
|
|
||||||
ssl.key = "/var/lib/acme/uninsane.org/key.pem";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
289
hosts/by-name/servo/services/prosody/default.nix
Normal file
289
hosts/by-name/servo/services/prosody/default.nix
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
# example configs:
|
||||||
|
# - official: <https://prosody.im/doc/example_config>
|
||||||
|
# - nixos: <https://github.com/kittywitch/nixfiles/blob/main/services/prosody.nix>
|
||||||
|
# config options:
|
||||||
|
# - <https://prosody.im/doc/configure>
|
||||||
|
#
|
||||||
|
# modules:
|
||||||
|
# - main: <https://prosody.im/doc/modules>
|
||||||
|
# - community: <https://modules.prosody.im/index.html>
|
||||||
|
#
|
||||||
|
# debugging:
|
||||||
|
# - logging:
|
||||||
|
# - enable `stanza_debug` module
|
||||||
|
# - enable `log.debug = "*syslog"` in extraConfig
|
||||||
|
# - interactive:
|
||||||
|
# - `telnet localhost 5582` (this is equal to `prosodyctl shell` -- but doesn't hang)
|
||||||
|
# - `watch:stanzas(target_spec, filter)` -> to log stanzas, for version > 0.12
|
||||||
|
# - console docs: <https://prosody.im/doc/console>
|
||||||
|
# - can modify/inspect arbitrary internals (lua) by prefixing line with `> `
|
||||||
|
# - e.g. `> _G` to print all globals
|
||||||
|
#
|
||||||
|
# sanity checks:
|
||||||
|
# - `sudo -u prosody -g prosody prosodyctl check connectivity`
|
||||||
|
# - `sudo -u prosody -g prosody prosodyctl check turn`
|
||||||
|
# - `sudo -u prosody -g prosody prosodyctl check turn -v --ping=stun.conversations.im`
|
||||||
|
# - checks that my stun/turn server is usable by clients of conversations.im (?)
|
||||||
|
# - `sudo -u prosody -g prosody prosodyctl check` (dns, config, certs)
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# create users with:
|
||||||
|
# - `sudo -u prosody prosodyctl adduser colin@uninsane.org`
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# federation/support matrix:
|
||||||
|
# - nixnet.services (runs ejabberd):
|
||||||
|
# - WORKS: sending and receiving PMs and calls (2023/10/15)
|
||||||
|
# - N.B.: it didn't originally work; was solved by disabling the lua-unbound DNS option & forcing the system/local resolver
|
||||||
|
# - cheogram (XMPP <-> SMS gateway):
|
||||||
|
# - WORKS: sending and receiving PMs, images (2023/10/15)
|
||||||
|
# - PARTIAL: calls (xmpp -> tel works; tel -> xmpp fails)
|
||||||
|
# - maybe i need to setup stun/turn
|
||||||
|
#
|
||||||
|
# TODO:
|
||||||
|
# - enable push notifications (mod_cloud_notify)
|
||||||
|
# - optimize coturn (e.g. move off of the VPN!)
|
||||||
|
# - ensure muc is working
|
||||||
|
# - enable file uploads
|
||||||
|
# - "upload.xmpp.uninsane.org:http_upload: URL: <https://upload.xmpp.uninsane.org:5281/upload> - Ensure this can be reached by users"
|
||||||
|
# - disable or fix bosh (jabber over http):
|
||||||
|
# - "certmanager: No certificate/key found for client_https port 0"
|
||||||
|
|
||||||
|
{ lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
# enables very verbose logging
|
||||||
|
enableDebug = false;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sane.persist.sys.byStore.plaintext = [
|
||||||
|
{ user = "prosody"; group = "prosody"; path = "/var/lib/prosody"; }
|
||||||
|
];
|
||||||
|
sane.ports.ports."5000" = {
|
||||||
|
protocol = [ "tcp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
|
description = "colin-xmpp-prosody-fileshare-proxy65";
|
||||||
|
};
|
||||||
|
sane.ports.ports."5222" = {
|
||||||
|
protocol = [ "tcp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
|
description = "colin-xmpp-client-to-server";
|
||||||
|
};
|
||||||
|
sane.ports.ports."5223" = {
|
||||||
|
protocol = [ "tcp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
|
description = "colin-xmpps-client-to-server"; # XMPP over TLS
|
||||||
|
};
|
||||||
|
sane.ports.ports."5269" = {
|
||||||
|
protocol = [ "tcp" ];
|
||||||
|
visibleTo.wan = true;
|
||||||
|
description = "colin-xmpp-server-to-server";
|
||||||
|
};
|
||||||
|
sane.ports.ports."5270" = {
|
||||||
|
protocol = [ "tcp" ];
|
||||||
|
visibleTo.wan = true;
|
||||||
|
description = "colin-xmpps-server-to-server"; # XMPP over TLS
|
||||||
|
};
|
||||||
|
sane.ports.ports."5280" = {
|
||||||
|
protocol = [ "tcp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
|
description = "colin-xmpp-bosh";
|
||||||
|
};
|
||||||
|
sane.ports.ports."5281" = {
|
||||||
|
protocol = [ "tcp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
|
description = "colin-xmpp-prosody-https"; # necessary?
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.prosody.extraGroups = [
|
||||||
|
"nginx" # provide access to certs
|
||||||
|
"ntfy-sh" # access to secret ntfy topic
|
||||||
|
];
|
||||||
|
|
||||||
|
security.acme.certs."uninsane.org".extraDomainNames = [
|
||||||
|
"xmpp.uninsane.org"
|
||||||
|
"conference.xmpp.uninsane.org"
|
||||||
|
"upload.xmpp.uninsane.org"
|
||||||
|
];
|
||||||
|
|
||||||
|
# exists so the XMPP server's cert can obtain altNames for all its resources
|
||||||
|
services.nginx.virtualHosts."xmpp.uninsane.org" = {
|
||||||
|
useACMEHost = "uninsane.org";
|
||||||
|
};
|
||||||
|
services.nginx.virtualHosts."conference.xmpp.uninsane.org" = {
|
||||||
|
useACMEHost = "uninsane.org";
|
||||||
|
};
|
||||||
|
services.nginx.virtualHosts."upload.xmpp.uninsane.org" = {
|
||||||
|
useACMEHost = "uninsane.org";
|
||||||
|
};
|
||||||
|
|
||||||
|
sane.dns.zones."uninsane.org".inet = {
|
||||||
|
# XXX: SRV records have to point to something with a A/AAAA record; no CNAMEs
|
||||||
|
A."xmpp" = "%ANATIVE%";
|
||||||
|
CNAME."conference.xmpp" = "xmpp";
|
||||||
|
CNAME."upload.xmpp" = "xmpp";
|
||||||
|
|
||||||
|
# _Service._Proto.Name TTL Class SRV Priority Weight Port Target
|
||||||
|
# - <https://xmpp.org/extensions/xep-0368.html>
|
||||||
|
# something's requesting the SRV records for conference.xmpp, so let's include it
|
||||||
|
# nothing seems to request XMPP SRVs for the other records (except @)
|
||||||
|
# lower numerical priority field tells clients to prefer this method
|
||||||
|
SRV."_xmpps-client._tcp.conference.xmpp" = "3 50 5223 xmpp";
|
||||||
|
SRV."_xmpps-server._tcp.conference.xmpp" = "3 50 5270 xmpp";
|
||||||
|
SRV."_xmpp-client._tcp.conference.xmpp" = "5 50 5222 xmpp";
|
||||||
|
SRV."_xmpp-server._tcp.conference.xmpp" = "5 50 5269 xmpp";
|
||||||
|
|
||||||
|
SRV."_xmpps-client._tcp" = "3 50 5223 xmpp";
|
||||||
|
SRV."_xmpps-server._tcp" = "3 50 5270 xmpp";
|
||||||
|
SRV."_xmpp-client._tcp" = "5 50 5222 xmpp";
|
||||||
|
SRV."_xmpp-server._tcp" = "5 50 5269 xmpp";
|
||||||
|
};
|
||||||
|
|
||||||
|
# help Prosody find its certificates.
|
||||||
|
# pointing it to /var/lib/acme doesn't quite work because it expects the private key
|
||||||
|
# to be named `privkey.pem` instead of acme's `key.pem`
|
||||||
|
# <https://prosody.im/doc/certificates#automatic_location>
|
||||||
|
sane.fs."/etc/prosody/certs/uninsane.org/fullchain.pem" = {
|
||||||
|
symlink.target = "/var/lib/acme/uninsane.org/fullchain.pem";
|
||||||
|
wantedBeforeBy = [ "prosody.service" ];
|
||||||
|
};
|
||||||
|
sane.fs."/etc/prosody/certs/uninsane.org/privkey.pem" = {
|
||||||
|
symlink.target = "/var/lib/acme/uninsane.org/key.pem";
|
||||||
|
wantedBeforeBy = [ "prosody.service" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.prosody = {
|
||||||
|
enable = true;
|
||||||
|
package = pkgs.prosody.override {
|
||||||
|
# XXX(2023/10/15): build without lua-unbound support.
|
||||||
|
# this forces Prosody to fall back to the default Lua DNS resolver, which seems more reliable.
|
||||||
|
# fixes errors like "unbound.queryXYZUV: Resolver error: out of memory"
|
||||||
|
# related: <https://issues.prosody.im/1737#comment-11>
|
||||||
|
lua.withPackages = selector: pkgs.lua.withPackages (p:
|
||||||
|
selector (p // { luaunbound = null; })
|
||||||
|
);
|
||||||
|
# withCommunityModules = [ "turncredentials" ];
|
||||||
|
};
|
||||||
|
admins = [ "colin@uninsane.org" ];
|
||||||
|
# allowRegistration = false; # defaults to false
|
||||||
|
|
||||||
|
muc = [
|
||||||
|
{
|
||||||
|
domain = "conference.xmpp.uninsane.org";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
uploadHttp.domain = "upload.xmpp.uninsane.org";
|
||||||
|
|
||||||
|
virtualHosts = {
|
||||||
|
# "Prosody requires at least one enabled VirtualHost to function. You can
|
||||||
|
# safely remove or disable 'localhost' once you have added another."
|
||||||
|
# localhost = {
|
||||||
|
# domain = "localhost";
|
||||||
|
# enabled = true;
|
||||||
|
# };
|
||||||
|
"xmpp.uninsane.org" = {
|
||||||
|
domain = "uninsane.org";
|
||||||
|
enabled = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
## modules:
|
||||||
|
# these are enabled by default, via <repo:nixos/nixpkgs:/pkgs/servers/xmpp/prosody/default.nix>
|
||||||
|
# - cloud_notify
|
||||||
|
# - http_upload
|
||||||
|
# - vcard_muc
|
||||||
|
# these are enabled by the module defaults (services.prosody.modules.<foo>)
|
||||||
|
# - admin_adhoc
|
||||||
|
# - blocklist
|
||||||
|
# - bookmarks
|
||||||
|
# - carbons
|
||||||
|
# - cloud_notify
|
||||||
|
# - csi
|
||||||
|
# - dialback
|
||||||
|
# - disco
|
||||||
|
# - http_files
|
||||||
|
# - mam
|
||||||
|
# - pep
|
||||||
|
# - ping
|
||||||
|
# - private
|
||||||
|
# - XEP-0049: let clients store arbitrary (private) data on the server
|
||||||
|
# - proxy65
|
||||||
|
# - XEP-0065: allow server to proxy file transfers between two clients who are behind NAT
|
||||||
|
# - register
|
||||||
|
# - roster
|
||||||
|
# - saslauth
|
||||||
|
# - smacks
|
||||||
|
# - time
|
||||||
|
# - tls
|
||||||
|
# - uptime
|
||||||
|
# - vcard_legacy
|
||||||
|
# - version
|
||||||
|
|
||||||
|
extraPluginPaths = [ ./modules ];
|
||||||
|
|
||||||
|
extraModules = [
|
||||||
|
# admin_shell: allows `prosodyctl shell` to work
|
||||||
|
# see: <https://prosody.im/doc/modules/mod_admin_shell>
|
||||||
|
# see: <https://prosody.im/doc/console>
|
||||||
|
"admin_shell"
|
||||||
|
"admin_telnet" #< needed by admin_shell
|
||||||
|
# lastactivity: XEP-0012: allow users to query how long another user has been idle for
|
||||||
|
# - not sure why i enabled this; think it was in someone's config i referenced
|
||||||
|
"lastactivity"
|
||||||
|
# allows prosody to share TURN/STUN secrets with XMPP clients to provide them access to the coturn server.
|
||||||
|
# see: <https://prosody.im/doc/coturn>
|
||||||
|
"turn_external"
|
||||||
|
# legacy coturn integration
|
||||||
|
# see: <https://modules.prosody.im/mod_turncredentials.html>
|
||||||
|
# "turncredentials"
|
||||||
|
"sane_ntfy"
|
||||||
|
] ++ lib.optionals enableDebug [
|
||||||
|
"stanza_debug" #< logs EVERY stanza as debug: <https://prosody.im/doc/modules/mod_stanza_debug>
|
||||||
|
];
|
||||||
|
|
||||||
|
extraConfig = ''
|
||||||
|
local function readAll(file)
|
||||||
|
local f = assert(io.open(file, "rb"))
|
||||||
|
local content = f:read("*all")
|
||||||
|
f:close()
|
||||||
|
-- remove trailing newline
|
||||||
|
return string.gsub(content, "%s+", "")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- logging docs:
|
||||||
|
-- - <https://prosody.im/doc/logging>
|
||||||
|
-- - <https://prosody.im/doc/advanced_logging>
|
||||||
|
-- levels: debug, info, warn, error
|
||||||
|
log = {
|
||||||
|
${if enableDebug then "debug" else "info"} = "*syslog";
|
||||||
|
}
|
||||||
|
|
||||||
|
-- see: <https://prosody.im/doc/certificates#automatic_location>
|
||||||
|
-- try to solve: "certmanager: Error indexing certificate directory /etc/prosody/certs: cannot open /etc/prosody/certs: No such file or directory"
|
||||||
|
-- only, this doesn't work because prosody doesn't like acme's naming scheme
|
||||||
|
-- certificates = "/var/lib/acme"
|
||||||
|
|
||||||
|
c2s_direct_tls_ports = { 5223 }
|
||||||
|
s2s_direct_tls_ports = { 5270 }
|
||||||
|
|
||||||
|
turn_external_host = "turn.uninsane.org"
|
||||||
|
turn_external_secret = readAll("/var/lib/coturn/shared_secret.bin")
|
||||||
|
-- turn_external_user = "prosody"
|
||||||
|
|
||||||
|
-- legacy mod_turncredentials integration
|
||||||
|
-- turncredentials_host = "turn.uninsane.org"
|
||||||
|
-- turncredentials_secret = readAll("/var/lib/coturn/shared_secret.bin")
|
||||||
|
|
||||||
|
ntfy_binary = "${pkgs.ntfy-sh}/bin/ntfy"
|
||||||
|
ntfy_topic = readAll("/run/secrets/ntfy-sh-topic")
|
||||||
|
|
||||||
|
-- s2s_require_encryption = true
|
||||||
|
-- c2s_require_encryption = true
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
@@ -0,0 +1,52 @@
|
|||||||
|
-- simple proof-of-concept Prosody module
|
||||||
|
-- module development guide: <https://prosody.im/doc/developers/modules>
|
||||||
|
-- module API docs: <https://prosody.im/doc/developers/moduleapi>
|
||||||
|
--
|
||||||
|
-- much of this code is lifted from Prosody's own `mod_cloud_notify`
|
||||||
|
|
||||||
|
local jid = require"util.jid";
|
||||||
|
|
||||||
|
local ntfy = module:get_option_string("ntfy_binary", "ntfy");
|
||||||
|
local ntfy_topic = module:get_option_string("ntfy_topic", "xmpp");
|
||||||
|
|
||||||
|
module:log("info", "initialized");
|
||||||
|
|
||||||
|
local function is_urgent(stanza)
|
||||||
|
if stanza.name == "message" then
|
||||||
|
if stanza:get_child("propose", "urn:xmpp:jingle-message:0") then
|
||||||
|
return true, "jingle call";
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function publish_ntfy(message)
|
||||||
|
-- message should be the message to publish
|
||||||
|
local ntfy_url = string.format("https://ntfy.uninsane.org/%s", ntfy_topic)
|
||||||
|
local cmd = string.format("%s pub %q %q", ntfy, ntfy_url, message)
|
||||||
|
module.log("debug", "invoking ntfy: %s", cmd)
|
||||||
|
local success, reason, code = os.execute(cmd)
|
||||||
|
if not success then
|
||||||
|
module:log("warn", "ntfy failed: %s => %s %d", cmd, reason, code)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function archive_message_added(event)
|
||||||
|
-- event is: { origin = origin, stanza = stanza, for_user = store_user, id = id }
|
||||||
|
local stanza = event.stanza;
|
||||||
|
local to = stanza.attr.to;
|
||||||
|
to = to and jid.split(to) or event.origin.username;
|
||||||
|
|
||||||
|
-- only notify if the stanza destination is the mam user we store it for
|
||||||
|
if event.for_user == to then
|
||||||
|
local is_urgent_stanza, urgent_reason = is_urgent(event.stanza);
|
||||||
|
|
||||||
|
if is_urgent_stanza then
|
||||||
|
module:log("info", "urgent push for %s (%s)", to, urgent_reason);
|
||||||
|
publish_ntfy(urgent_reason)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
module:hook("archive-message-added", archive_message_added);
|
@@ -1,12 +1,27 @@
|
|||||||
{ pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.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;
|
||||||
@@ -18,6 +17,7 @@
|
|||||||
protocol = [ "udp" "tcp" ];
|
protocol = [ "udp" "tcp" ];
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
visibleTo.wan = true;
|
visibleTo.wan = true;
|
||||||
|
visibleTo.ovpn = true;
|
||||||
description = "colin-dns-hosting";
|
description = "colin-dns-hosting";
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -34,18 +34,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 +64,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 +96,113 @@
|
|||||||
-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/ \
|
||||||
|
-e s/%AOVPNS%/185.157.162.178/ \
|
||||||
|
${zoneTemplate} > ${zoneFor flavor}
|
||||||
|
'';
|
||||||
|
serviceConfig = config.systemd.services.trust-dns.serviceConfig // {
|
||||||
|
ExecStart = ''
|
||||||
|
${pkgs.trust-dns}/bin/${pkgs.trust-dns.meta.mainProgram} \
|
||||||
|
--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
|
||||||
@@ -40,6 +41,14 @@
|
|||||||
# does the builder use some content-addressed db to efficiently dedupe?
|
# does the builder use some content-addressed db to efficiently dedupe?
|
||||||
nix.settings.auto-optimise-store = true;
|
nix.settings.auto-optimise-store = true;
|
||||||
|
|
||||||
|
services.journald.extraConfig = ''
|
||||||
|
# docs: `man journald.conf`
|
||||||
|
# merged journald config is deployed to /etc/systemd/journald.conf
|
||||||
|
[Journal]
|
||||||
|
# disable journal compression because the underlying fs is compressed
|
||||||
|
Compress=no
|
||||||
|
'';
|
||||||
|
|
||||||
systemd.services.nix-daemon.serviceConfig = {
|
systemd.services.nix-daemon.serviceConfig = {
|
||||||
# the nix-daemon manages nix builders
|
# the nix-daemon manages nix builders
|
||||||
# kill nix-daemon subprocesses when systemd-oomd detects an out-of-memory condition
|
# kill nix-daemon subprocesses when systemd-oomd detects an out-of-memory condition
|
||||||
|
@@ -57,84 +57,56 @@ let
|
|||||||
};
|
};
|
||||||
|
|
||||||
podcasts = [
|
podcasts = [
|
||||||
(fromDb "lexfridman.com/podcast" // rat)
|
(fromDb "acquiredlpbonussecretsecret.libsyn.com" // tech) # ACQ2 - more "Acquired" episodes
|
||||||
## Astral Codex Ten
|
|
||||||
(fromDb "sscpodcast.libsyn.com" // rat)
|
|
||||||
## Less Wrong Curated
|
|
||||||
(fromDb "feeds.libsyn.com/421877" // rat)
|
|
||||||
## Econ Talk
|
|
||||||
(fromDb "feeds.simplecast.com/wgl4xEgL" // rat)
|
|
||||||
## Cory Doctorow -- both podcast & text entries
|
|
||||||
(fromDb "craphound.com" // pol)
|
|
||||||
## Maggie Killjoy -- referenced by Cory Doctorow
|
|
||||||
(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 "werenotwrong.fireside.fm" // pol)
|
|
||||||
(fromDb "usefulidiots.substack.com" // pol)
|
|
||||||
# (mkPod "https://podcasts.la.utexas.edu/this-is-democracy/feed/podcast/" // pol // weekly)
|
|
||||||
## Civboot -- https://anchor.fm/civboot
|
|
||||||
(fromDb "anchor.fm/s/34c7232c/podcast/rss" // tech)
|
|
||||||
## Emerge: making sense of what's next -- <https://www.whatisemerging.com/emergepodcast>
|
|
||||||
(mkPod "https://anchor.fm/s/21bc734/podcast/rss" // pol // infrequent)
|
|
||||||
(fromDb "feeds.feedburner.com/80000HoursPodcast" // rat)
|
|
||||||
## Daniel Huberman on sleep
|
|
||||||
(fromDb "feeds.megaphone.fm/hubermanlab" // uncat)
|
|
||||||
## Multidisciplinary Association for Psychedelic Studies
|
|
||||||
(fromDb "mapspodcast.libsyn.com" // uncat)
|
|
||||||
(fromDb "allinchamathjason.libsyn.com" // pol)
|
(fromDb "allinchamathjason.libsyn.com" // pol)
|
||||||
(fromDb "feeds.transistor.fm/acquired" // tech)
|
(fromDb "anchor.fm/s/34c7232c/podcast/rss" // tech) # Civboot -- https://anchor.fm/civboot
|
||||||
## ACQ2 - more "Acquired" episodes
|
|
||||||
(fromDb "acquiredlpbonussecretsecret.libsyn.com" // tech)
|
|
||||||
# The Intercept - Deconstructed
|
|
||||||
(fromDb "rss.acast.com/deconstructed")
|
|
||||||
# (fromDb "rss.prod.firstlook.media/deconstructed/podcast.rss" // pol) #< possible URL rot
|
|
||||||
## The Daily
|
|
||||||
(mkPod "https://feeds.simplecast.com/54nAGcIl" // pol // daily)
|
|
||||||
# The Intercept - Intercepted
|
|
||||||
(fromDb "rss.acast.com/intercepted-with-jeremy-scahill")
|
|
||||||
# (fromDb "rss.prod.firstlook.media/intercepted/podcast.rss" // pol) #< possible URL rot
|
|
||||||
(fromDb "podcast.posttv.com/itunes/post-reports.xml" // pol)
|
|
||||||
## Eric Weinstein
|
|
||||||
(fromDb "rss.art19.com/the-portal" // rat)
|
|
||||||
(fromDb "darknetdiaries.com" // tech)
|
|
||||||
## Radiolab -- also available here, but ONLY OVER HTTP: <http://feeds.wnyc.org/radiolab>
|
|
||||||
(fromDb "feeds.feedburner.com/radiolab" // pol)
|
|
||||||
## Sam Harris
|
|
||||||
(fromDb "wakingup.libsyn.com" // pol)
|
|
||||||
## 99% Invisible -- also available here: <https://feeds.simplecast.com/BqbsxVfO>
|
|
||||||
(fromDb "feeds.99percentinvisible.org/99percentinvisible" // pol)
|
|
||||||
(fromDb "rss.acast.com/ft-tech-tonic" // tech)
|
|
||||||
(fromDb "feeds.feedburner.com/dancarlin/history" // rat)
|
|
||||||
(fromDb "rss.art19.com/60-minutes" // pol)
|
|
||||||
## The Verge - Decoder
|
|
||||||
(fromDb "feeds.megaphone.fm/recodedecode" // tech)
|
|
||||||
## Matrix (chat) Live
|
|
||||||
(fromDb "feed.podbean.com/matrixlive/feed.xml" // tech)
|
|
||||||
(fromDb "cast.postmarketos.org" // tech)
|
(fromDb "cast.postmarketos.org" // tech)
|
||||||
|
(fromDb "congressionaldish.libsyn.com" // pol) # Jennifer Briney
|
||||||
|
(fromDb "craphound.com" // pol) # Cory Doctorow -- both podcast & text entries
|
||||||
|
(fromDb "darknetdiaries.com" // tech)
|
||||||
|
(fromDb "feed.podbean.com/matrixlive/feed.xml" // tech) # Matrix (chat) Live
|
||||||
|
(fromDb "feeds.99percentinvisible.org/99percentinvisible" // pol) # 99% Invisible -- also available here: <https://feeds.simplecast.com/BqbsxVfO>
|
||||||
|
(fromDb "feeds.feedburner.com/80000HoursPodcast" // rat)
|
||||||
|
(fromDb "feeds.feedburner.com/dancarlin/history" // rat)
|
||||||
|
(fromDb "feeds.feedburner.com/radiolab" // pol) # Radiolab -- also available here, but ONLY OVER HTTP: <http://feeds.wnyc.org/radiolab>
|
||||||
|
(fromDb "feeds.libsyn.com/421877" // rat) # Less Wrong Curated
|
||||||
|
(fromDb "feeds.megaphone.fm/behindthebastards" // pol) # also Maggie Killjoy
|
||||||
|
(fromDb "feeds.megaphone.fm/hubermanlab" // uncat) # Daniel Huberman on sleep
|
||||||
|
(fromDb "feeds.megaphone.fm/recodedecode" // tech) # The Verge - Decoder
|
||||||
|
(fromDb "feeds.simplecast.com/54nAGcIl" // pol) # The Daily
|
||||||
|
(fromDb "feeds.simplecast.com/82FI35Px" // pol) # Ezra Klein Show
|
||||||
|
(fromDb "feeds.simplecast.com/wgl4xEgL" // rat) # Econ Talk
|
||||||
|
(fromDb "feeds.simplecast.com/xKJ93w_w" // uncat) # Atlas Obscura
|
||||||
|
(fromDb "feeds.transistor.fm/acquired" // tech)
|
||||||
|
(fromDb "lexfridman.com/podcast" // rat)
|
||||||
|
(fromDb "mapspodcast.libsyn.com" // uncat) # Multidisciplinary Association for Psychedelic Studies
|
||||||
|
(fromDb "omegataupodcast.net" // tech) # 3/4 German; 1/4 eps are English
|
||||||
|
(fromDb "omny.fm/shows/cool-people-who-did-cool-stuff" // pol) # Maggie Killjoy -- referenced by Cory Doctorow
|
||||||
|
(fromDb "podcast.posttv.com/itunes/post-reports.xml" // pol)
|
||||||
(fromDb "podcast.thelinuxexp.com" // tech)
|
(fromDb "podcast.thelinuxexp.com" // tech)
|
||||||
## Michael Malice - Your Welcome -- also available here: <https://origin.podcastone.com/podcast?categoryID2=2232>
|
(fromDb "politicalorphanage.libsyn.com" // pol)
|
||||||
(fromDb "rss.art19.com/your-welcome" // pol)
|
(fromDb "reverseengineering.libsyn.com/rss" // tech) # UnNamed Reverse Engineering Podcast
|
||||||
|
(fromDb "rss.acast.com/deconstructed") # The Intercept - Deconstructed
|
||||||
|
(fromDb "rss.acast.com/ft-tech-tonic" // tech)
|
||||||
|
(fromDb "rss.acast.com/intercepted-with-jeremy-scahill") # The Intercept - Intercepted
|
||||||
|
(fromDb "rss.art19.com/60-minutes" // pol)
|
||||||
|
(fromDb "rss.art19.com/the-portal" // rat) # Eric Weinstein
|
||||||
(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)
|
(fromDb "sharkbytes.transistor.fm" // tech) # Wireshark Podcast o_0
|
||||||
(fromDb "talesfromthebridge.buzzsprout.com" // tech)
|
(fromDb "sscpodcast.libsyn.com" // rat) # Astral Codex Ten
|
||||||
## UnNamed Reverse Engineering Podcast
|
(fromDb "talesfromthebridge.buzzsprout.com" // tech) # Sci-Fi? has Peter Watts; author of No Moods, Ads or Cutesy Fucking Icons (rifters.com)
|
||||||
(fromDb "reverseengineering.libsyn.com/rss" // tech)
|
(fromDb "techwontsave.us" // pol) # rec by Cory Doctorow
|
||||||
## The Witch Trials of J.K. Rowling
|
# (fromDb "trashfuturepodcast.podbean.com" // pol) # rec by Cory Doctorow, but way rambly
|
||||||
## - <https://www.thefp.com/witchtrials>
|
(fromDb "wakingup.libsyn.com" // pol) # Sam Harris
|
||||||
(mkPod "https://feeds.megaphone.fm/RUNMED9919162779" // pol // infrequent)
|
(fromDb "werenotwrong.fireside.fm" // pol)
|
||||||
## Atlas Obscura
|
|
||||||
(fromDb "feeds.simplecast.com/xKJ93w_w" // uncat)
|
# (fromDb "rss.art19.com/your-welcome" // pol) # Michael Malice - Your Welcome -- also available here: <https://origin.podcastone.com/podcast?categoryID2=2232>
|
||||||
## Ezra Klein Show
|
# (fromDb "rss.prod.firstlook.media/deconstructed/podcast.rss" // pol) #< possible URL rot
|
||||||
(fromDb "feeds.simplecast.com/82FI35Px" // pol)
|
# (fromDb "rss.prod.firstlook.media/intercepted/podcast.rss" // pol) #< possible URL rot
|
||||||
## Wireshark Podcast o_0
|
# (mkPod "https://anchor.fm/s/21bc734/podcast/rss" // pol // infrequent) # Emerge: making sense of what's next -- <https://www.whatisemerging.com/emergepodcast>
|
||||||
(fromDb "sharkbytes.transistor.fm" // tech)
|
# (mkPod "https://audioboom.com/channels/5097784.rss" // tech) # Lateral with Tom Scott
|
||||||
## 3/4 German; 1/4 eps are English
|
# (mkPod "https://feeds.megaphone.fm/RUNMED9919162779" // pol // infrequent) # The Witch Trials of J.K. Rowling: <https://www.thefp.com/witchtrials>
|
||||||
(fromDb "omegataupodcast.net" // tech)
|
# (mkPod "https://podcasts.la.utexas.edu/this-is-democracy/feed/podcast/" // pol // weekly)
|
||||||
## Lateral with Tom Scott
|
|
||||||
(mkPod "https://audioboom.com/channels/5097784.rss" // tech)
|
|
||||||
];
|
];
|
||||||
|
|
||||||
texts = [
|
texts = [
|
||||||
@@ -150,7 +122,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)
|
||||||
@@ -166,6 +138,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
|
||||||
@@ -176,6 +150,7 @@ let
|
|||||||
(fromDb "vitalik.ca" // tech)
|
(fromDb "vitalik.ca" // tech)
|
||||||
## ian (Sanctuary)
|
## ian (Sanctuary)
|
||||||
(fromDb "sagacioussuricata.com" // tech)
|
(fromDb "sagacioussuricata.com" // tech)
|
||||||
|
(fromDb "artemis.sh" // tech)
|
||||||
## Bunnie Juang
|
## Bunnie Juang
|
||||||
(fromDb "bunniestudios.com" // tech)
|
(fromDb "bunniestudios.com" // tech)
|
||||||
(fromDb "blog.danieljanus.pl" // tech)
|
(fromDb "blog.danieljanus.pl" // tech)
|
||||||
@@ -186,6 +161,8 @@ let
|
|||||||
(mkText "https://anish.lakhwara.com/home.html" // tech // weekly)
|
(mkText "https://anish.lakhwara.com/home.html" // tech // weekly)
|
||||||
(fromDb "jefftk.com" // tech)
|
(fromDb "jefftk.com" // tech)
|
||||||
(fromDb "pomeroyb.com" // tech)
|
(fromDb "pomeroyb.com" // tech)
|
||||||
|
(fromDb "harihareswara.net" // tech // pol) # rec by Cory Doctorow
|
||||||
|
(fromDb "mako.cc/copyrighteous" // tech // pol) # rec by Cory Doctorow
|
||||||
# (mkText "https://til.simonwillison.net/tils/feed.atom" // tech // weekly)
|
# (mkText "https://til.simonwillison.net/tils/feed.atom" // tech // weekly)
|
||||||
|
|
||||||
# TECH PROJECTS
|
# TECH PROJECTS
|
||||||
@@ -251,6 +228,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)
|
||||||
|
@@ -111,6 +111,12 @@ lib.mkMerge [
|
|||||||
fsType = "nfs";
|
fsType = "nfs";
|
||||||
options = fsOpts.nfs ++ fsOpts.auto ++ fsOpts.wg;
|
options = fsOpts.nfs ++ fsOpts.auto ++ fsOpts.wg;
|
||||||
};
|
};
|
||||||
|
fileSystems."/mnt/servo-nfs/playground" = {
|
||||||
|
device = "servo-hn:/playground";
|
||||||
|
noCheck = true;
|
||||||
|
fsType = "nfs";
|
||||||
|
options = fsOpts.nfs ++ fsOpts.auto ++ fsOpts.wg;
|
||||||
|
};
|
||||||
# fileSystems."/mnt/servo-media-nfs" = {
|
# fileSystems."/mnt/servo-media-nfs" = {
|
||||||
# device = "servo-hn:/media";
|
# device = "servo-hn:/media";
|
||||||
# noCheck = true;
|
# noCheck = true;
|
||||||
|
@@ -1,44 +0,0 @@
|
|||||||
{ lib, pkgs, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
boot.initrd.supportedFilesystems = [ "ext4" "btrfs" "ext2" "ext3" "vfat" ];
|
|
||||||
# useful emergency utils
|
|
||||||
boot.initrd.extraUtilsCommands = ''
|
|
||||||
copy_bin_and_libs ${pkgs.btrfs-progs}/bin/btrfstune
|
|
||||||
'';
|
|
||||||
boot.kernelParams = [ "boot.shell_on_fail" ];
|
|
||||||
# other kernelParams:
|
|
||||||
# "boot.trace"
|
|
||||||
# "systemd.log_level=debug"
|
|
||||||
# "systemd.log_target=console"
|
|
||||||
|
|
||||||
# hack in the `boot.shell_on_fail` arg since that doesn't always seem to work.
|
|
||||||
boot.initrd.preFailCommands = "allowShell=1";
|
|
||||||
|
|
||||||
# default: 4 (warn). 7 is debug
|
|
||||||
boot.consoleLogLevel = 7;
|
|
||||||
|
|
||||||
boot.loader.grub.enable = lib.mkDefault false;
|
|
||||||
boot.loader.generic-extlinux-compatible.enable = lib.mkDefault true;
|
|
||||||
|
|
||||||
# non-free firmware
|
|
||||||
hardware.enableRedistributableFirmware = true;
|
|
||||||
|
|
||||||
# powertop will default to putting USB devices -- including HID -- to sleep after TWO SECONDS
|
|
||||||
powerManagement.powertop.enable = false;
|
|
||||||
|
|
||||||
services.logind.extraConfig = ''
|
|
||||||
# don’t shutdown when power button is short-pressed
|
|
||||||
HandlePowerKey=ignore
|
|
||||||
'';
|
|
||||||
|
|
||||||
# services.snapper.configs = {
|
|
||||||
# root = {
|
|
||||||
# subvolume = "/";
|
|
||||||
# extraConfig = {
|
|
||||||
# ALLOW_USERS = "colin";
|
|
||||||
# };
|
|
||||||
# };
|
|
||||||
# };
|
|
||||||
# services.snapper.snapshotInterval = "daily";
|
|
||||||
}
|
|
73
hosts/common/hardware/default.nix
Normal file
73
hosts/common/hardware/default.nix
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
{ lib, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./x86_64.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
boot.initrd.supportedFilesystems = [ "ext4" "btrfs" "ext2" "ext3" "vfat" ];
|
||||||
|
# useful emergency utils
|
||||||
|
boot.initrd.extraUtilsCommands = ''
|
||||||
|
copy_bin_and_libs ${pkgs.btrfs-progs}/bin/btrfstune
|
||||||
|
copy_bin_and_libs ${pkgs.util-linux}/bin/{cfdisk,lsblk,lscpu}
|
||||||
|
copy_bin_and_libs ${pkgs.gptfdisk}/bin/{cgdisk,gdisk}
|
||||||
|
copy_bin_and_libs ${pkgs.smartmontools}/bin/smartctl
|
||||||
|
copy_bin_and_libs ${pkgs.nvme-cli}/bin/nvme
|
||||||
|
copy_bin_and_libs ${pkgs.e2fsprogs}/bin/resize2fs
|
||||||
|
'';
|
||||||
|
boot.kernelParams = [
|
||||||
|
"boot.shell_on_fail"
|
||||||
|
#v experimental full pre-emption for hopefully better call/audio latency on moby.
|
||||||
|
# also toggleable at runtime via /sys/kernel/debug/sched/preempt
|
||||||
|
# defaults to preempt=voluntary
|
||||||
|
# "preempt=full"
|
||||||
|
];
|
||||||
|
# other kernelParams:
|
||||||
|
# "boot.trace"
|
||||||
|
# "systemd.log_level=debug"
|
||||||
|
# "systemd.log_target=console"
|
||||||
|
|
||||||
|
# hack in the `boot.shell_on_fail` arg since that doesn't always seem to work.
|
||||||
|
boot.initrd.preFailCommands = "allowShell=1";
|
||||||
|
|
||||||
|
# default: 4 (warn). 7 is debug
|
||||||
|
boot.consoleLogLevel = 7;
|
||||||
|
|
||||||
|
boot.loader.grub.enable = lib.mkDefault false;
|
||||||
|
boot.loader.generic-extlinux-compatible.enable = lib.mkDefault true;
|
||||||
|
|
||||||
|
# non-free firmware
|
||||||
|
hardware.enableRedistributableFirmware = true;
|
||||||
|
|
||||||
|
# powertop will default to putting USB devices -- including HID -- to sleep after TWO SECONDS
|
||||||
|
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 = ''
|
||||||
|
# don’t shutdown when power button is short-pressed
|
||||||
|
HandlePowerKey=ignore
|
||||||
|
'';
|
||||||
|
|
||||||
|
# services.snapper.configs = {
|
||||||
|
# root = {
|
||||||
|
# subvolume = "/";
|
||||||
|
# extraConfig = {
|
||||||
|
# ALLOW_USERS = "colin";
|
||||||
|
# };
|
||||||
|
# };
|
||||||
|
# };
|
||||||
|
# services.snapper.snapshotInterval = "daily";
|
||||||
|
}
|
@@ -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;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
@@ -7,7 +7,7 @@ let
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
sane.user.persist.private = [ ".local/share/keyrings" ];
|
sane.user.persist.byStore.private = [ ".local/share/keyrings" ];
|
||||||
|
|
||||||
sane.user.fs."private/.local/share/keyrings/default" = {
|
sane.user.fs."private/.local/share/keyrings/default" = {
|
||||||
generated.command = [ "${init-keyring}/bin/init-keyring" ];
|
generated.command = [ "${init-keyring}/bin/init-keyring" ];
|
||||||
|
@@ -13,7 +13,7 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
# ssh key is stored in private storage
|
# ssh key is stored in private storage
|
||||||
sane.user.persist.private = [
|
sane.user.persist.byStore.private = [
|
||||||
{ type = "file"; path = ".ssh/id_ed25519"; }
|
{ type = "file"; path = ".ssh/id_ed25519"; }
|
||||||
];
|
];
|
||||||
sane.user.fs.".ssh/id_ed25519.pub" = lib.mkIf (user-pubkey != null) {
|
sane.user.fs.".ssh/id_ed25519.pub" = lib.mkIf (user-pubkey != null) {
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
@@ -5,12 +5,12 @@
|
|||||||
# store /home/colin/a/b in /home/private/a/b instead of /home/private/home/colin/a/b
|
# store /home/colin/a/b in /home/private/a/b instead of /home/private/home/colin/a/b
|
||||||
sane.persist.stores.private.prefix = "/home/colin";
|
sane.persist.stores.private.prefix = "/home/colin";
|
||||||
|
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
# TODO: these should be private.. somehow
|
# TODO: these should be private.. somehow
|
||||||
"/var/log"
|
"/var/log"
|
||||||
"/var/backup" # for e.g. postgres dumps
|
"/var/backup" # for e.g. postgres dumps
|
||||||
];
|
];
|
||||||
sane.persist.sys.cryptClearOnBoot = [
|
sane.persist.sys.byStore.cryptClearOnBoot = [
|
||||||
"/var/lib/systemd/coredump"
|
"/var/lib/systemd/coredump"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
{ pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
declPackageSet = pkgs: {
|
declPackageSet = pkgs: {
|
||||||
@@ -44,10 +44,11 @@ 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.wipe-flare"
|
||||||
|
"sane-scripts.wipe-fractal"
|
||||||
];
|
];
|
||||||
"sane-scripts.sys-utils" = declPackageSet [
|
"sane-scripts.sys-utils" = declPackageSet [
|
||||||
"sane-scripts.ip-port-forward"
|
"sane-scripts.ip-port-forward"
|
||||||
@@ -59,21 +60,27 @@ in
|
|||||||
"btrfs-progs"
|
"btrfs-progs"
|
||||||
"cacert.unbundled" # some services require unbundled /etc/ssl/certs
|
"cacert.unbundled" # some services require unbundled /etc/ssl/certs
|
||||||
"cryptsetup"
|
"cryptsetup"
|
||||||
|
"ddrescue"
|
||||||
"dig"
|
"dig"
|
||||||
|
"dtc" # device tree [de]compiler
|
||||||
|
"e2fsprogs" # resize2fs
|
||||||
"efibootmgr"
|
"efibootmgr"
|
||||||
|
"ethtool"
|
||||||
"fatresize"
|
"fatresize"
|
||||||
"fd"
|
"fd"
|
||||||
"file"
|
"file"
|
||||||
# "fwupd"
|
# "fwupd"
|
||||||
"gawk"
|
"gawk"
|
||||||
|
"gdb" # to debug segfaults
|
||||||
"git"
|
"git"
|
||||||
"gptfdisk"
|
"gptfdisk" # gdisk
|
||||||
"hdparm"
|
"hdparm"
|
||||||
"htop"
|
"htop"
|
||||||
"iftop"
|
"iftop"
|
||||||
"inetutils" # for telnet
|
"inetutils" # for telnet
|
||||||
"iotop"
|
"iotop"
|
||||||
"iptables"
|
"iptables"
|
||||||
|
"iw"
|
||||||
"jq"
|
"jq"
|
||||||
"killall"
|
"killall"
|
||||||
"lsof"
|
"lsof"
|
||||||
@@ -84,6 +91,7 @@ in
|
|||||||
"netcat"
|
"netcat"
|
||||||
"nethogs"
|
"nethogs"
|
||||||
"nmap"
|
"nmap"
|
||||||
|
"nvme-cli" # nvme
|
||||||
"openssl"
|
"openssl"
|
||||||
"parted"
|
"parted"
|
||||||
"pciutils"
|
"pciutils"
|
||||||
@@ -91,13 +99,14 @@ in
|
|||||||
"pstree"
|
"pstree"
|
||||||
"ripgrep"
|
"ripgrep"
|
||||||
"screen"
|
"screen"
|
||||||
"smartmontools"
|
"smartmontools" # smartctl
|
||||||
"socat"
|
"socat"
|
||||||
"strace"
|
"strace"
|
||||||
"subversion"
|
"subversion"
|
||||||
"tcpdump"
|
"tcpdump"
|
||||||
"tree"
|
"tree"
|
||||||
"usbutils"
|
"usbutils"
|
||||||
|
"util-linux" # lsblk, lscpu, etc
|
||||||
"wget"
|
"wget"
|
||||||
"wirelesstools" # iwlist
|
"wirelesstools" # iwlist
|
||||||
];
|
];
|
||||||
@@ -115,11 +124,13 @@ in
|
|||||||
# - debugging?
|
# - debugging?
|
||||||
consoleUtils = declPackageSet [
|
consoleUtils = declPackageSet [
|
||||||
"alsaUtils" # for aplay, speaker-test
|
"alsaUtils" # for aplay, speaker-test
|
||||||
|
"binutils-unwrapped" # for strings; though this brings 80MB of unrelated baggage too
|
||||||
# "cdrtools"
|
# "cdrtools"
|
||||||
"clinfo"
|
"clinfo"
|
||||||
"dmidecode"
|
"dmidecode"
|
||||||
"dtrx" # `unar` alternative, "Do The Right eXtraction"
|
"dtrx" # `unar` alternative, "Do The Right eXtraction"
|
||||||
"efivar"
|
"efivar"
|
||||||
|
"eza" # a better 'ls'
|
||||||
# "flashrom"
|
# "flashrom"
|
||||||
"git" # needed as a user package, for config.
|
"git" # needed as a user package, for config.
|
||||||
# "gnupg"
|
# "gnupg"
|
||||||
@@ -127,11 +138,11 @@ in
|
|||||||
# "gopass"
|
# "gopass"
|
||||||
# "gopass-jsonapi"
|
# "gopass-jsonapi"
|
||||||
"helix" # text editor
|
"helix" # text editor
|
||||||
# "kitty" # XXX needs to be in consolueUtils because `ssh servo` from kitty sets `TERM=xterm-kitty` in the remote 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"
|
||||||
# "memtester"
|
# "memtester"
|
||||||
|
"mercurial" # hg
|
||||||
"neovim" # needed as a user package, for swap persistence
|
"neovim" # needed as a user package, for swap persistence
|
||||||
# "nettools"
|
# "nettools"
|
||||||
# "networkmanager"
|
# "networkmanager"
|
||||||
@@ -141,7 +152,7 @@ in
|
|||||||
# "oathToolkit" # for oathtool
|
# "oathToolkit" # for oathtool
|
||||||
# "ponymix"
|
# "ponymix"
|
||||||
"pulsemixer"
|
"pulsemixer"
|
||||||
"python3"
|
"python3-repl"
|
||||||
# "python3Packages.eyeD3" # music tagging
|
# "python3Packages.eyeD3" # music tagging
|
||||||
"ripgrep" # needed as a user package so that its user-level config file can be installed
|
"ripgrep" # needed as a user package so that its user-level config file can be installed
|
||||||
"rsync"
|
"rsync"
|
||||||
@@ -154,8 +165,8 @@ in
|
|||||||
"sudo"
|
"sudo"
|
||||||
# "tageditor" # music tagging
|
# "tageditor" # music tagging
|
||||||
# "unar"
|
# "unar"
|
||||||
|
"unzip"
|
||||||
"wireguard-tools"
|
"wireguard-tools"
|
||||||
"xdg-terminal-exec"
|
|
||||||
"xdg-utils" # for xdg-open
|
"xdg-utils" # for xdg-open
|
||||||
# "yarn"
|
# "yarn"
|
||||||
"zsh"
|
"zsh"
|
||||||
@@ -193,61 +204,74 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
devPkgs = declPackageSet [
|
devPkgs = declPackageSet [
|
||||||
|
"cargo"
|
||||||
"clang"
|
"clang"
|
||||||
|
"lua"
|
||||||
"nodejs"
|
"nodejs"
|
||||||
|
"patchelf"
|
||||||
|
"rustc"
|
||||||
"tree-sitter"
|
"tree-sitter"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
# INDIVIDUAL PACKAGE DEFINITIONS
|
# INDIVIDUAL PACKAGE DEFINITIONS
|
||||||
|
|
||||||
|
cargo.persist.byStore.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.byStore.private = [ ".config/discord" ];
|
||||||
|
|
||||||
# `emote` will show a first-run dialog based on what's in this directory.
|
# `emote` will show a first-run dialog based on what's in this directory.
|
||||||
# mostly, it just keeps a LRU of previously-used emotes to optimize display order.
|
# mostly, it just keeps a LRU of previously-used emotes to optimize display order.
|
||||||
# TODO: package [smile](https://github.com/mijorus/smile) for probably a better mobile experience.
|
# TODO: package [smile](https://github.com/mijorus/smile) for probably a better mobile experience.
|
||||||
emote.persist.plaintext = [ ".local/share/Emote" ];
|
emote.persist.byStore.plaintext = [ ".local/share/Emote" ];
|
||||||
|
|
||||||
fluffychat-moby.persist.plaintext = [ ".local/share/chat.fluffy.fluffychat" ];
|
fluffychat-moby.persist.byStore.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.byStore.private = [ ".config/gh" ];
|
||||||
|
|
||||||
|
"gnome.gnome-maps".persist.byStore.plaintext = [ ".cache/shumate" ];
|
||||||
|
"gnome.gnome-maps".persist.byStore.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.byStore.plaintext = [ ".bitmonero" ];
|
||||||
|
|
||||||
mumble.persist.private = [ ".local/share/Mumble" ];
|
mumble.persist.byStore.private = [ ".local/share/Mumble" ];
|
||||||
|
|
||||||
# settings (electron app)
|
# settings (electron app)
|
||||||
obsidian.persist.plaintext = [ ".config/obsidian" ];
|
obsidian.persist.byStore.plaintext = [ ".config/obsidian" ];
|
||||||
|
|
||||||
|
python3-repl.package = pkgs.python3.withPackages (ps: with ps; [
|
||||||
|
requests
|
||||||
|
]);
|
||||||
|
|
||||||
# creds, media
|
# creds, media
|
||||||
signal-desktop.persist.private = [ ".config/Signal" ];
|
signal-desktop.persist.byStore.private = [ ".config/Signal" ];
|
||||||
|
|
||||||
# printer/filament settings
|
# printer/filament settings
|
||||||
slic3r.persist.plaintext = [ ".Slic3r" ];
|
slic3r.persist.byStore.plaintext = [ ".Slic3r" ];
|
||||||
|
|
||||||
# creds, widevine .so download. TODO: could easily manage these statically.
|
tdesktop.persist.byStore.private = [ ".local/share/TelegramDesktop" ];
|
||||||
spotify.persist.plaintext = [ ".config/spotify" ];
|
|
||||||
|
|
||||||
tdesktop.persist.private = [ ".local/share/TelegramDesktop" ];
|
tokodon.persist.byStore.private = [ ".cache/KDE/tokodon" ];
|
||||||
|
|
||||||
tokodon.persist.private = [ ".cache/KDE/tokodon" ];
|
whalebird.persist.byStore.private = [ ".config/Whalebird" ];
|
||||||
|
|
||||||
# hardenedMalloc solves an "unable to connect to Tor" error when pressing the "connect" button
|
yarn.persist.byStore.plaintext = [ ".cache/yarn" ];
|
||||||
# - still required as of 2023/07/14
|
|
||||||
tor-browser-bundle-bin.package = pkgs.tor-browser-bundle-bin.override {
|
|
||||||
useHardenedMalloc = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
whalebird.persist.private = [ ".config/Whalebird" ];
|
|
||||||
|
|
||||||
yarn.persist.plaintext = [ ".cache/yarn" ];
|
|
||||||
|
|
||||||
# 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.byStore.private = [ ".zcash" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
programs.feedbackd = lib.mkIf config.sane.programs.feedbackd.enabled {
|
||||||
|
enable = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
103
hosts/common/programs/bemenu.nix
Normal file
103
hosts/common/programs/bemenu.nix
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
{ lib, pkgs, ... }:
|
||||||
|
# notable bemenu options:
|
||||||
|
# - see `bemenu --help` for all
|
||||||
|
# -P, --prefix text to show before highlighted item.
|
||||||
|
# --scrollbar display scrollbar. (none (default), always, autohide)
|
||||||
|
# -H, --line-height defines the height to make each menu line (0 = default height). (wx)
|
||||||
|
# -M, --margin defines the empty space on either side of the menu. (wx)
|
||||||
|
# -W, --width-factor defines the relative width factor of the menu (from 0 to 1). (wx)
|
||||||
|
# -B, --border defines the width of the border in pixels around the menu. (wx)
|
||||||
|
# -R --border-radius defines the radius of the border around the menu (0 = no curved borders).
|
||||||
|
# --ch defines the height of the cursor (0 = scales with line height). (wx)
|
||||||
|
# --cw defines the width of the cursor. (wx)
|
||||||
|
# --hp defines the horizontal padding for the entries in single line mode. (wx)
|
||||||
|
# --fn defines the font to be used ('name [size]'). (wx)
|
||||||
|
# --tb defines the title background color. (wx)
|
||||||
|
# --tf defines the title foreground color. (wx)
|
||||||
|
# --fb defines the filter background color. (wx)
|
||||||
|
# --ff defines the filter foreground color. (wx)
|
||||||
|
# --nb defines the normal background color. (wx)
|
||||||
|
# --nf defines the normal foreground color. (wx)
|
||||||
|
# --hb defines the highlighted background color. (wx)
|
||||||
|
# --hf defines the highlighted foreground color. (wx)
|
||||||
|
# --fbb defines the feedback background color. (wx)
|
||||||
|
# --fbf defines the feedback foreground color. (wx)
|
||||||
|
# --sb defines the selected background color. (wx)
|
||||||
|
# --sf defines the selected foreground color. (wx)
|
||||||
|
# --ab defines the alternating background color. (wx)
|
||||||
|
# --af defines the alternating foreground color. (wx)
|
||||||
|
# --scb defines the scrollbar background color. (wx)
|
||||||
|
# --scf defines the scrollbar foreground color. (wx)
|
||||||
|
# --bdr defines the border color. (wx)
|
||||||
|
#
|
||||||
|
# colors are specified as `#RRGGBB`
|
||||||
|
# defaults:
|
||||||
|
# --ab "#222222"
|
||||||
|
# --af "#bbbbbb"
|
||||||
|
# --bdr "#005577"
|
||||||
|
# --border 3
|
||||||
|
# --cb "#222222"
|
||||||
|
# --center
|
||||||
|
# --cf "#bbbbbb"
|
||||||
|
# --fb "#222222"
|
||||||
|
# --fbb "#eeeeee"
|
||||||
|
# --fbf "#222222"
|
||||||
|
# --ff "#bbbbbb"
|
||||||
|
# --fixed-height
|
||||||
|
# --fn 'Sxmo 14'
|
||||||
|
# --hb "#005577"
|
||||||
|
# --hf "#eeeeee"
|
||||||
|
# --line-height 20
|
||||||
|
# --list 16
|
||||||
|
# --margin 40
|
||||||
|
# --nb "#222222"
|
||||||
|
# --nf "#bbbbbb"
|
||||||
|
# --no-overlap
|
||||||
|
# --no-spacing
|
||||||
|
# --sb "#323232"
|
||||||
|
# --scb "#005577"
|
||||||
|
# --scf "#eeeeee"
|
||||||
|
# --scrollbar autohide
|
||||||
|
# --tb "#005577"
|
||||||
|
# --tf "#eeeeee"
|
||||||
|
# --wrap
|
||||||
|
let
|
||||||
|
bg = "#1d1721"; # slight purple
|
||||||
|
fg0 = "#d8d8d8"; # inactive text (light grey)
|
||||||
|
fg1 = "#ffffff"; # active text (white)
|
||||||
|
accent0 = "#1f5e54"; # darker but saturated teal
|
||||||
|
accent1 = "#418379"; # teal (matches nixos-bg)
|
||||||
|
accent2 = "#5b938a"; # brighter but muted teal
|
||||||
|
bemenuArgs = [
|
||||||
|
"--wrap --scrollbar autohide --fixed-height"
|
||||||
|
"--center --margin 45"
|
||||||
|
"--no-spacing"
|
||||||
|
# XXX: font size doesn't seem to take effect (would prefer larger)
|
||||||
|
"--fn 'monospace 14' --line-height 22 --border 3"
|
||||||
|
"--bdr '${accent0}'" # border
|
||||||
|
"--scf '${accent2}' --scb '${accent0}'" # scrollbar
|
||||||
|
"--tb '${accent0}' --tf '${fg0}'" # title
|
||||||
|
"--fb '${accent0}' --ff '${fg1}'" # filter (i.e. text that's been entered)
|
||||||
|
"--hb '${accent1}' --hf '${fg1}'" # selected item
|
||||||
|
"--nb '${bg}' --nf '${fg0}'" # normal lines (even)
|
||||||
|
"--ab '${bg}' --af '${fg0}'" # alternated lines (odd)
|
||||||
|
"--cf '${accent0}' --cb '${accent0}'" # cursor (not very useful)
|
||||||
|
];
|
||||||
|
bemenuOpts = lib.concatStringsSep " " bemenuArgs;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sane.programs.bemenu = {
|
||||||
|
package = pkgs.bemenu.overrideAttrs (upstream: {
|
||||||
|
nativeBuildInputs = (upstream.nativeBuildInputs or []) ++ [
|
||||||
|
pkgs.makeWrapper
|
||||||
|
];
|
||||||
|
# can alternatively be specified as CLI flags
|
||||||
|
postInstall = (upstream.postInstall or "") + ''
|
||||||
|
wrapProgram $out/bin/bemenu \
|
||||||
|
--set BEMENU_OPTS "${bemenuOpts}"
|
||||||
|
wrapProgram $out/bin/bemenu-run \
|
||||||
|
--set BEMENU_OPTS "${bemenuOpts}"
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
9
hosts/common/programs/brave.nix
Normal file
9
hosts/common/programs/brave.nix
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
sane.programs.brave = {
|
||||||
|
persist.byStore.cryptClearOnBoot = [
|
||||||
|
".cache/BraveSoftware"
|
||||||
|
".config/BraveSoftware"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
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.byStore.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;
|
||||||
|
};
|
||||||
|
}
|
@@ -4,7 +4,7 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
sane.programs.cantata = {
|
sane.programs.cantata = {
|
||||||
persist.plaintext = [
|
persist.byStore.plaintext = [
|
||||||
".cache/cantata" # album art
|
".cache/cantata" # album art
|
||||||
".local/share/cantata/library" # library index (?)
|
".local/share/cantata/library" # library index (?)
|
||||||
];
|
];
|
||||||
|
@@ -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;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -36,7 +36,7 @@ in
|
|||||||
# package = chattyNoOauth;
|
# package = chattyNoOauth;
|
||||||
package = chatty-latest;
|
package = chatty-latest;
|
||||||
suggestedPrograms = [ "gnome-keyring" ];
|
suggestedPrograms = [ "gnome-keyring" ];
|
||||||
persist.private = [
|
persist.byStore.private = [
|
||||||
".local/share/chatty" # matrix avatars and files
|
".local/share/chatty" # matrix avatars and files
|
||||||
# not just XMPP; without this Chatty will regenerate its device-id every boot.
|
# 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/ contains XMPP *and* Matrix auth, logs, avatar cache, and a bit more
|
||||||
|
@@ -27,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 +37,14 @@ conky.config = {
|
|||||||
|
|
||||||
-- texeci <interval_sec> <cmd>: run the command periodically, _in a separate thread_ so as not to block rendering
|
-- 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 Sxmo:size=20:style=Bold}${alignc}${texeci 600 @weather@ }${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,7 +3,7 @@
|
|||||||
{
|
{
|
||||||
sane.programs.cozy = {
|
sane.programs.cozy = {
|
||||||
# cozy uses a sqlite db for its config and exposes no CLI options other than --help and --debug
|
# cozy uses a sqlite db for its config and exposes no CLI options other than --help and --debug
|
||||||
persist.plaintext = [
|
persist.byStore.plaintext = [
|
||||||
".local/share/cozy" # sqlite db (config & index?)
|
".local/share/cozy" # sqlite db (config & index?)
|
||||||
".cache/cozy" # offline cache
|
".cache/cozy" # offline cache
|
||||||
];
|
];
|
||||||
|
@@ -5,18 +5,26 @@
|
|||||||
./aerc.nix
|
./aerc.nix
|
||||||
./alacritty.nix
|
./alacritty.nix
|
||||||
./assorted.nix
|
./assorted.nix
|
||||||
|
./bemenu.nix
|
||||||
|
./brave.nix
|
||||||
|
./calls.nix
|
||||||
./cantata.nix
|
./cantata.nix
|
||||||
./chatty.nix
|
./chatty.nix
|
||||||
|
./conky
|
||||||
./cozy.nix
|
./cozy.nix
|
||||||
./dino.nix
|
./dino.nix
|
||||||
./element-desktop.nix
|
./element-desktop.nix
|
||||||
./epiphany.nix
|
./epiphany.nix
|
||||||
./evince.nix
|
./evince.nix
|
||||||
|
./feedbackd.nix
|
||||||
./firefox.nix
|
./firefox.nix
|
||||||
|
./flare-signal.nix
|
||||||
./fontconfig.nix
|
./fontconfig.nix
|
||||||
./fractal.nix
|
./fractal.nix
|
||||||
./fwupd.nix
|
./fwupd.nix
|
||||||
./g4music.nix
|
./g4music.nix
|
||||||
|
./gajim.nix
|
||||||
|
./geary.nix
|
||||||
./git.nix
|
./git.nix
|
||||||
./gnome-feeds.nix
|
./gnome-feeds.nix
|
||||||
./gnome-keyring.nix
|
./gnome-keyring.nix
|
||||||
@@ -26,11 +34,11 @@
|
|||||||
./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
|
./mopidy.nix
|
||||||
@@ -40,15 +48,21 @@
|
|||||||
./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
|
./rhythmbox.nix
|
||||||
./ripgrep.nix
|
./ripgrep.nix
|
||||||
./sfeed.nix
|
./sfeed.nix
|
||||||
./splatmoji.nix
|
./splatmoji.nix
|
||||||
|
./spotify.nix
|
||||||
./steam.nix
|
./steam.nix
|
||||||
|
./stepmania.nix
|
||||||
./sublime-music.nix
|
./sublime-music.nix
|
||||||
|
./swaynotificationcenter.nix
|
||||||
./tangram.nix
|
./tangram.nix
|
||||||
|
./tor-browser-bundle-bin.nix
|
||||||
./tuba.nix
|
./tuba.nix
|
||||||
./vlc.nix
|
./vlc.nix
|
||||||
./wireshark.nix
|
./wireshark.nix
|
||||||
|
@@ -9,7 +9,63 @@
|
|||||||
# - ensure the other party is in your roster
|
# - ensure the other party is in your roster
|
||||||
# - open a DM with the party
|
# - open a DM with the party
|
||||||
# - click the phone icon at top (only visible if other party is in your roster)
|
# - 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
|
||||||
|
# - possibly this is solved by enabling RealtimeKit (rtkit)
|
||||||
|
# - TODO: see if Dino calls work better with `echo full > /sys/kernel/debug/sched/preempt`
|
||||||
|
#
|
||||||
|
# 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.persist.private = [ ".local/share/dino" ];
|
sane.programs.dino = {
|
||||||
|
configOption = with lib; mkOption {
|
||||||
|
default = {};
|
||||||
|
type = types.submodule {
|
||||||
|
options.autostart = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
persist.byStore.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,8 +1,14 @@
|
|||||||
|
# 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 = {
|
||||||
# creds/session keys, etc
|
# creds/session keys, etc
|
||||||
persist.private = [ ".config/Element" ];
|
persist.byStore.private = [ ".config/Element" ];
|
||||||
|
|
||||||
suggestedPrograms = [ "gnome-keyring" ];
|
suggestedPrograms = [ "gnome-keyring" ];
|
||||||
};
|
};
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
sane.programs.epiphany = {
|
sane.programs.epiphany = {
|
||||||
# XXX(2023/07/08): running on moby without this hack fails, with:
|
# XXX(2023/07/08): running on moby without `WEBKIT_DISABLE_SANDBOX...` fails, with:
|
||||||
# - `bwrap: Can't make symlink at /var/run: File exists`
|
# - `bwrap: Can't make symlink at /var/run: File exists`
|
||||||
# this could be due to:
|
# this could be due to:
|
||||||
# - epiphany is somewhere following a symlink into /var/run instead of /run
|
# - epiphany is somewhere following a symlink into /var/run instead of /run
|
||||||
@@ -19,6 +19,9 @@
|
|||||||
# - <https://gitlab.gnome.org/GNOME/gnome-builder/-/issues/1164>
|
# - <https://gitlab.gnome.org/GNOME/gnome-builder/-/issues/1164>
|
||||||
# - <https://github.com/flatpak/flatpak/issues/3477>
|
# - <https://github.com/flatpak/flatpak/issues/3477>
|
||||||
# - <https://github.com/NixOS/nixpkgs/issues/197085>
|
# - <https://github.com/NixOS/nixpkgs/issues/197085>
|
||||||
|
#
|
||||||
|
# TODO: consider `WEBKIT_USE_SINGLE_WEB_PROCESS=1` for better perf
|
||||||
|
# - this runs all tabs in 1 process. which is fine, if i'm not a heavy multi-tabber
|
||||||
package = pkgs.epiphany.overrideAttrs (upstream: {
|
package = pkgs.epiphany.overrideAttrs (upstream: {
|
||||||
preFixup = ''
|
preFixup = ''
|
||||||
gappsWrapperArgs+=(
|
gappsWrapperArgs+=(
|
||||||
@@ -26,7 +29,7 @@
|
|||||||
);
|
);
|
||||||
'' + (upstream.preFixup or "");
|
'' + (upstream.preFixup or "");
|
||||||
});
|
});
|
||||||
persist.private = [
|
persist.byStore.private = [
|
||||||
".cache/epiphany"
|
".cache/epiphany"
|
||||||
".local/share/epiphany"
|
".local/share/epiphany"
|
||||||
# also .config/epiphany, but appears empty
|
# also .config/epiphany, but appears empty
|
||||||
|
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 = {};
|
||||||
|
};
|
||||||
|
}
|
@@ -225,6 +225,12 @@ in
|
|||||||
// treat it as unrevoked.
|
// treat it as unrevoked.
|
||||||
// see: <https://librewolf.net/docs/faq/#im-getting-sec_error_ocsp_server_error-what-can-i-do>
|
// see: <https://librewolf.net/docs/faq/#im-getting-sec_error_ocsp_server_error-what-can-i-do>
|
||||||
defaultPref("security.OCSP.require", false);
|
defaultPref("security.OCSP.require", false);
|
||||||
|
|
||||||
|
// scrollbar configuration, see: <https://artemis.sh/2023/10/12/scrollbars.html>
|
||||||
|
// style=4 gives rectangular scrollbars
|
||||||
|
// could also enable "always show scrollbars" in about:preferences -- not sure what the actual pref name for that is
|
||||||
|
defaultPref("widget.non-native-theme.scrollbar.size.override", 50);
|
||||||
|
defaultPref("widget.non-native-theme.scrollbar.style", 4);
|
||||||
'';
|
'';
|
||||||
fs."${cfg.browser.dotDir}/default".dir = {};
|
fs."${cfg.browser.dotDir}/default".dir = {};
|
||||||
# instruct Firefox to put the profile in a predictable directory (so we can do things like persist just it).
|
# instruct Firefox to put the profile in a predictable directory (so we can do things like persist just it).
|
||||||
|
47
hosts/common/programs/flare-signal.nix
Normal file
47
hosts/common/programs/flare-signal.nix
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Flare is a 3rd-party GTK4 Signal app.
|
||||||
|
# UI is effectively a clone of Fractal.
|
||||||
|
#
|
||||||
|
# compatibility:
|
||||||
|
# - desko: works fine. pairs, and exchanges contact list (but not message history) with the paired device. exchanges future messages fine.
|
||||||
|
# - moby (cross compiled flare-signal-nixified): nope. it pairs, but can only *receive* messages and never *send* them.
|
||||||
|
# - even `rsync`ing the data and keyrings from desko -> moby, still fails in that same manner.
|
||||||
|
# - console shows error messages. quite possibly an endianness mismatch somewhere
|
||||||
|
# - moby (partially-emulated flare-signal): works! pairs and can send/receive messages, same as desko.
|
||||||
|
#
|
||||||
|
# error signatures (to reset, run `sane-wipe-fractal`):
|
||||||
|
# - upon sending a message, the other side receives it, but Signal desktop gets "A message from Colin could not be delivered" and the local CLI shows:
|
||||||
|
# ```
|
||||||
|
# ERROR libsignal_service::websocket] SignalWebSocket: Websocket error: SignalWebSocket: end of application request stream; socket closing
|
||||||
|
# ERROR presage::manager] Error opening envelope: ProtobufDecodeError(DecodeError { description: "invalid tag value: 0", stack: [("Content", "data_message")] }), message will be skipped!
|
||||||
|
# ERROR presage::manager] Error opening envelope: ProtobufDecodeError(DecodeError { description: "invalid tag value: 0", stack: [("Content", "data_message")] }), message will be skipped!
|
||||||
|
# ```
|
||||||
|
# - this occurs on moby, desko, `flare-signal` and `flare-signal-nixified`
|
||||||
|
# - the Websocket error seems to be unrelated, occurs during normal/good operation
|
||||||
|
# - related issues: <https://github.com/whisperfish/presage/issues/152>
|
||||||
|
# error when sending from Flare to other Flare device:
|
||||||
|
# - ```
|
||||||
|
# ERROR libsignal_protocol::session_cipher] Message from <UUID>.3 failed to decrypt; sender ratchet public key <key> message counter 1
|
||||||
|
# No current session
|
||||||
|
# ERROR presage::manager] Error opening envelope: SignalProtocolError(InvalidKyberPreKeyId), message will be skipped!
|
||||||
|
# ```
|
||||||
|
# - but signal iOS will still read it.
|
||||||
|
#
|
||||||
|
# well, seems to have unpredictable errors particularly when being used on multiple devices.
|
||||||
|
# desktop _seems_ more reliable than on mobile, but not confident.
|
||||||
|
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
sane.programs.flare-signal = {
|
||||||
|
package = pkgs.flare-signal-nixified;
|
||||||
|
# package = pkgs.flare-signal;
|
||||||
|
persist.byStore.private = [
|
||||||
|
# everything: conf, state, files, all opaque
|
||||||
|
".local/share/flare"
|
||||||
|
# also persists a secret in ~/.local/share/keyrings. reset with:
|
||||||
|
# - `secret-tool search --all --unlock 'xdg:schema' 'de.schmidhuberj.Flare'`
|
||||||
|
# - `secret-tool clear 'xdg:schema' 'de.schmidhuberj.Flare'`
|
||||||
|
# and it persists some dconf settings (e.g. device name). reset with:
|
||||||
|
# - `dconf reset -f /de/schmidhuberj/Flare/`.
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
@@ -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,60 @@
|
|||||||
{ pkgs, ... }:
|
# Fractal: GTK4 instant messenger client for the Matrix protocol
|
||||||
|
#
|
||||||
|
# very susceptible to state corruption during hard power-cycles.
|
||||||
|
# if it stalls while launching, especially with a brief message at bottom
|
||||||
|
# "unable to open store"
|
||||||
|
# then:
|
||||||
|
# - remove ~/.local/share/stable/*
|
||||||
|
# - this might give I/O error, in which case remove the corresponding path under
|
||||||
|
# /nix/persist/home/colin/private (which can be found by correlating timestamps/sizes with that in ~/private/.local/share/stable).
|
||||||
|
# - reboot (maybe necessary).
|
||||||
|
# - now you can send messages, and read messages in unencrypted rooms, but not read messages from encrypted rooms.
|
||||||
|
# to fix encrypted message receipt:
|
||||||
|
# - start from above (fractal closed, no ~/.local/share/stable/*)
|
||||||
|
# - in ~/.local/share/keyrings/Default_keyring.keyring:
|
||||||
|
# - find the entry that says "display-name=Fractal: Matrix credentials for <mxid>"
|
||||||
|
# - remove that entry and all associated entries (i.e. ones with same number but different :attributeN)
|
||||||
|
# - REBOOT. otherwise keyring stuff seems to stay cached in RAM
|
||||||
|
# - login to Fractal. give an hour to sync.
|
||||||
|
# - it'll kick you back to a page asking you to cross-sign. open FluffyChat and do the emoji compare. success!
|
||||||
|
{ 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.byStore.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";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
sane.programs.g4music = {
|
sane.programs.g4music = {
|
||||||
persist.plaintext = [
|
persist.byStore.plaintext = [
|
||||||
# index?
|
# index?
|
||||||
".cache/com.github.neithern.g4music"
|
".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.byStore.private = [
|
||||||
|
# avatars, thumbnails...
|
||||||
|
".cache/gajim"
|
||||||
|
# sqlite database labeled "settings". definitely includes UI theming
|
||||||
|
".config/gajim"
|
||||||
|
# omemo keys, downloads, logs
|
||||||
|
".local/share/gajim"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
55
hosts/common/programs/geary.nix
Normal file
55
hosts/common/programs/geary.nix
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# geary is a gtk3 email client.
|
||||||
|
# outstanding issues:
|
||||||
|
# - it uses webkitgtk_4_1, which is expensive to build.
|
||||||
|
# could be upgraded to webkitgtk latest if upgraded to gtk4
|
||||||
|
# <https://gitlab.gnome.org/GNOME/geary/-/issues/1212>
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
sane.programs."gnome.geary" = {
|
||||||
|
persist.byStore.private = [
|
||||||
|
# attachments, and email -- contained in a sqlite db
|
||||||
|
".local/share/geary"
|
||||||
|
# also `.cache/geary/web-resources`, which tends to stay << 1 MiB
|
||||||
|
];
|
||||||
|
fs.".config/geary/account_01/geary.ini".symlink.text = ''
|
||||||
|
[Metadata]
|
||||||
|
version=1
|
||||||
|
status=enabled
|
||||||
|
|
||||||
|
[Account]
|
||||||
|
ordinal=2
|
||||||
|
label=
|
||||||
|
# 14 = "fetch last 14d of mail every time i connect"
|
||||||
|
# -1 = "fetch *all* mail"
|
||||||
|
prefetch_days=-1
|
||||||
|
save_drafts=true
|
||||||
|
save_sent=true
|
||||||
|
use_signature=false
|
||||||
|
signature=
|
||||||
|
sender_mailboxes=colin@uninsane.org;
|
||||||
|
service_provider=other
|
||||||
|
|
||||||
|
[Folders]
|
||||||
|
archive_folder=Archive;
|
||||||
|
drafts_folder=
|
||||||
|
sent_folder=
|
||||||
|
junk_folder=
|
||||||
|
trash_folder=
|
||||||
|
|
||||||
|
[Incoming]
|
||||||
|
login=colin
|
||||||
|
remember_password=true
|
||||||
|
host=imap.uninsane.org
|
||||||
|
port=993
|
||||||
|
transport_security=transport
|
||||||
|
credentials=custom
|
||||||
|
|
||||||
|
[Outgoing]
|
||||||
|
remember_password=true
|
||||||
|
host=mx.uninsane.org
|
||||||
|
port=465
|
||||||
|
transport_security=transport
|
||||||
|
credentials=use-incoming
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
@@ -3,7 +3,7 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
sane.programs.gnome-weather = {
|
sane.programs.gnome-weather = {
|
||||||
persist.plaintext = [
|
persist.byStore.plaintext = [
|
||||||
".cache/libgweather"
|
".cache/libgweather"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
# gnome feeds RSS viewer
|
# help:
|
||||||
|
# - #gpodder on irc.libera.chat
|
||||||
{ config, pkgs, sane-lib, ... }:
|
{ config, pkgs, sane-lib, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
@@ -7,12 +8,22 @@ let
|
|||||||
wanted-feeds = feeds.filterByFormat ["podcast"] all-feeds;
|
wanted-feeds = feeds.filterByFormat ["podcast"] all-feeds;
|
||||||
in {
|
in {
|
||||||
sane.programs.gpodder = {
|
sane.programs.gpodder = {
|
||||||
package = pkgs.gpodder-adaptive-configured;
|
package = pkgs.gpodder-adaptive-configured.overrideAttrs (base: {
|
||||||
|
# environment variables:
|
||||||
|
# - GPODDER_HOME (defaults to "~/gPodder")
|
||||||
|
# - GPODDER_DOWNLOAD_DIR (defaults to "$GPODDER_HOME/Downloads")
|
||||||
|
# - GPODDER_WRITE_LOGS ("yes" or "no")
|
||||||
|
# - GPODDER_EXTENSIONS
|
||||||
|
# - GPODDER_DISABLE_EXTENSIONS ("yes" or "no")
|
||||||
|
extraMakeWrapperArgs = (base.extraMakeWrapperArgs or []) ++ [
|
||||||
|
"--set" "GPODDER_HOME" "~/.local/share/gPodder"
|
||||||
|
];
|
||||||
|
});
|
||||||
# package = pkgs.gpodder-configured;
|
# package = pkgs.gpodder-configured;
|
||||||
fs.".config/gpodderFeeds.opml".symlink.text = feeds.feedsToOpml wanted-feeds;
|
fs.".config/gpodderFeeds.opml".symlink.text = feeds.feedsToOpml wanted-feeds;
|
||||||
|
|
||||||
# XXX: we preserve the whole thing because if we only preserve gPodder/Downloads
|
# XXX: we preserve the whole thing because if we only preserve gPodder/Downloads
|
||||||
# then startup is SLOW during feed import, and we might end up with zombie eps in the dl dir.
|
# then startup is SLOW during feed import, and we might end up with zombie eps in the dl dir.
|
||||||
persist.plaintext = [ "gPodder" ];
|
persist.byStore.plaintext = [ ".local/share/gPodder" ];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
# grammars need to be persisted when developing them
|
# grammars need to be persisted when developing them
|
||||||
# - `hx --grammar fetch` and `hx --grammar build`
|
# - `hx --grammar fetch` and `hx --grammar build`
|
||||||
# but otherwise, they ship as part of HELIX_RUNTIME, in the nix store
|
# but otherwise, they ship as part of HELIX_RUNTIME, in the nix store
|
||||||
# persist.plaintext = [ ".config/helix/runtime/grammars" ];
|
# persist.byStore.plaintext = [ ".config/helix/runtime/grammars" ];
|
||||||
fs.".config/helix/config.toml".symlink.text = ''
|
fs.".config/helix/config.toml".symlink.text = ''
|
||||||
# docs: <https://docs.helix-editor.com/configuration.html>
|
# docs: <https://docs.helix-editor.com/configuration.html>
|
||||||
[editor.soft-wrap]
|
[editor.soft-wrap]
|
||||||
|
@@ -10,6 +10,6 @@
|
|||||||
# jellyfin stores things in a bunch of directories: this one persists auth info.
|
# jellyfin stores things in a bunch of directories: this one persists auth info.
|
||||||
# it *might* be possible to populate this externally (it's Qt stuff), but likely to
|
# it *might* be possible to populate this externally (it's Qt stuff), but likely to
|
||||||
# be fragile and take an hour+ to figure out.
|
# be fragile and take an hour+ to figure out.
|
||||||
persist.plaintext = [ ".local/share/Jellyfin Media Player" ];
|
persist.byStore.plaintext = [ ".local/share/Jellyfin Media Player" ];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -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
|
|
||||||
}
|
|
@@ -3,6 +3,6 @@
|
|||||||
sane.programs.komikku = {
|
sane.programs.komikku = {
|
||||||
secrets.".local/share/komikku/keyrings/plaintext.keyring" = ../../../secrets/common/komikku_accounts.json.bin;
|
secrets.".local/share/komikku/keyrings/plaintext.keyring" = ../../../secrets/common/komikku_accounts.json.bin;
|
||||||
# downloads end up here, and without the toplevel database komikku doesn't know they exist.
|
# downloads end up here, and without the toplevel database komikku doesn't know they exist.
|
||||||
persist.plaintext = [ ".local/share/komikku" ];
|
persist.byStore.plaintext = [ ".local/share/komikku" ];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -26,9 +26,8 @@ in {
|
|||||||
package = pkgs.koreader-from-src;
|
package = pkgs.koreader-from-src;
|
||||||
# koreader applies these lua "patches" at boot:
|
# koreader applies these lua "patches" at boot:
|
||||||
# - <https://github.com/koreader/koreader/wiki/User-patches>
|
# - <https://github.com/koreader/koreader/wiki/User-patches>
|
||||||
# - TODO: upstream this patch to koreader
|
# - 2023/10/29: koreader code hasn't changed, but somehow FTP browser seems usable even without the isConnected patch now.
|
||||||
# fs.".config/koreader/patches".symlink.target = "${./.}";
|
# fs.".config/koreader/patches/2-colin-NetworkManager-isConnected.lua".symlink.target = "${./2-colin-NetworkManager-isConnected.lua}";
|
||||||
fs.".config/koreader/patches/2-colin-NetworkManager-isConnected.lua".symlink.target = "${./2-colin-NetworkManager-isConnected.lua}";
|
|
||||||
|
|
||||||
# koreader news plugin, enabled by default. file format described here:
|
# koreader news plugin, enabled by default. file format described here:
|
||||||
# - <repo:koreader/koreader:plugins/newsdownloader.koplugin/feed_config.lua>
|
# - <repo:koreader/koreader:plugins/newsdownloader.koplugin/feed_config.lua>
|
||||||
@@ -37,12 +36,14 @@ in {
|
|||||||
${lib.concatStringsSep ",\n " koreaderRssEntries}
|
${lib.concatStringsSep ",\n " koreaderRssEntries}
|
||||||
}--do NOT change this line
|
}--do NOT change this line
|
||||||
'';
|
'';
|
||||||
|
# easier to navigate via filebrowser than finding the news menu entry
|
||||||
|
fs."Books/rss-koreader".symlink.target = "../.config/koreader/news";
|
||||||
|
|
||||||
# koreader on aarch64 errors if there's no fonts directory (sandboxing thing, i guess)
|
# koreader on aarch64 errors if there's no fonts directory (sandboxing thing, i guess)
|
||||||
fs.".local/share/fonts".dir = {};
|
fs.".local/share/fonts".dir = {};
|
||||||
|
|
||||||
# history, cache, dictionaries...
|
# history, cache, dictionaries...
|
||||||
# could be more explicit if i symlinked the history.lua file to somewhere it can persist better.
|
# could be more explicit if i symlinked the history.lua file to somewhere it can persist better.
|
||||||
persist.plaintext = [ ".config/koreader" ];
|
persist.byStore.plaintext = [ ".config/koreader" ];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,6 @@
|
|||||||
{
|
{
|
||||||
sane.programs.lemoa = {
|
sane.programs.lemoa = {
|
||||||
# creds
|
# creds
|
||||||
persist.private = [ ".local/share/io.github.lemmygtk.lemoa" ];
|
persist.byStore.private = [ ".local/share/io.github.lemmygtk.lemoa" ];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
sane.programs.mepo = {
|
sane.programs.mepo = {
|
||||||
persist.plaintext = [ ".cache/mepo/tiles" ];
|
persist.byStore.plaintext = [ ".cache/mepo/tiles" ];
|
||||||
# ~/.cache/mepo/savestate has precise coordinates and pins: keep those private
|
# ~/.cache/mepo/savestate has precise coordinates and pins: keep those private
|
||||||
persist.private = [
|
persist.byStore.private = [
|
||||||
{ type = "file"; path = ".cache/mepo/savestate"; }
|
{ type = "file"; path = ".cache/mepo/savestate"; }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -42,10 +42,10 @@ in
|
|||||||
# mopidy-moped: <https://github.com/martijnboland/moped>
|
# mopidy-moped: <https://github.com/martijnboland/moped>
|
||||||
# mopidy-muse: <https://github.com/cristianpb/muse>
|
# mopidy-muse: <https://github.com/cristianpb/muse>
|
||||||
]);
|
]);
|
||||||
persist.plaintext = [
|
persist.byStore.plaintext = [
|
||||||
".local/share/mopidy/local" # thumbs, library db
|
".local/share/mopidy/local" # thumbs, library db
|
||||||
];
|
];
|
||||||
persist.private = [
|
persist.byStore.private = [
|
||||||
".local/share/mopidy/http" # cookie
|
".local/share/mopidy/http" # cookie
|
||||||
];
|
];
|
||||||
secrets.".config/mopidy/mopidy.conf" = ../../../secrets/common/mopidy.conf.bin;
|
secrets.".config/mopidy/mopidy.conf" = ../../../secrets/common/mopidy.conf.bin;
|
||||||
|
@@ -3,45 +3,62 @@
|
|||||||
# - <https://github.com/mpv-player/mpv/wiki>
|
# - <https://github.com/mpv-player/mpv/wiki>
|
||||||
# curated mpv mods/scripts/users:
|
# curated mpv mods/scripts/users:
|
||||||
# - <https://github.com/stax76/awesome-mpv>
|
# - <https://github.com/stax76/awesome-mpv>
|
||||||
{ pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.sane.programs.mpv;
|
||||||
|
in
|
||||||
{
|
{
|
||||||
sane.programs.mpv = {
|
sane.programs.mpv = {
|
||||||
|
configOption = with lib; mkOption {
|
||||||
|
default = {};
|
||||||
|
type = types.submodule {
|
||||||
|
options.vo = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = "--vo=FOO flag to pass to 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 = [
|
extraMakeWrapperArgs = lib.optionals (cfg.config.vo != null) [
|
||||||
# 2023/08/29: fixes an error where mpv on moby launches with the message
|
# 2023/08/29: fixes an error where mpv on moby launches with the message
|
||||||
# "DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory"
|
# "DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory"
|
||||||
# audio still works, and controls, screenshotting, etc -- just not the actual rendering
|
# audio still works, and controls, screenshotting, etc -- just not the actual rendering
|
||||||
# this is likely a regression for mpv 0.36.0.
|
# 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.
|
# the actual error message *appears* to come from the mesa library, but it's tough to trace.
|
||||||
|
#
|
||||||
|
# backend compatibility (2023/10/22):
|
||||||
# run with `--vo=help` to see a list of all output options.
|
# run with `--vo=help` to see a list of all output options.
|
||||||
# non-exhaustive (F=fails, W=works)
|
# non-exhaustive (W=works, F=fails, A=audio-only, U=audio+ui only (no video))
|
||||||
# ? 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
|
# ? null Null video output
|
||||||
# ? caca libcaca
|
# A (default)
|
||||||
|
# A dmabuf-wayland Wayland dmabuf video output
|
||||||
|
# A libmpv render API for libmpv (mpv plays the audio, but doesn't even render a window)
|
||||||
|
# A vdpau VDPAU with X11
|
||||||
# F drm Direct Rendering Manager (software scaling)
|
# F drm Direct Rendering Manager (software scaling)
|
||||||
"--add-flags" "--vo=sdl"
|
# F gpu-next Video output based on libplacebo
|
||||||
|
# F vaapi VA API with X11
|
||||||
|
# F x11 X11 (software scaling)
|
||||||
|
# F xv X11/Xv
|
||||||
|
# U gpu Shader-based GPU Renderer
|
||||||
|
# W caca libcaca (terminal rendering)
|
||||||
|
# W sdl SDL 2.0 Renderer
|
||||||
|
# W wlshm Wayland SHM video output (software scaling)
|
||||||
|
"--add-flags" "--vo=${cfg.config.vo}"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
persist.plaintext = [ ".config/mpv/watch_later" ];
|
persist.byStore.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
|
||||||
'';
|
'';
|
||||||
@@ -96,7 +113,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";
|
||||||
|
@@ -87,10 +87,14 @@ in
|
|||||||
{
|
{
|
||||||
# private because there could be sensitive things in the swap
|
# private because there could be sensitive things in the swap
|
||||||
sane.programs.neovim = {
|
sane.programs.neovim = {
|
||||||
persist.private = [ ".cache/vim-swap" ];
|
persist.byStore.private = [ ".cache/vim-swap" ];
|
||||||
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 {
|
||||||
|
@@ -7,7 +7,7 @@ let
|
|||||||
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
|
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
|
||||||
in {
|
in {
|
||||||
sane.programs.newsflash = {
|
sane.programs.newsflash = {
|
||||||
persist.plaintext = [ ".local/share/news-flash" ];
|
persist.byStore.plaintext = [ ".local/share/news-flash" ];
|
||||||
fs.".config/newsflashFeeds.opml".symlink.text =
|
fs.".config/newsflashFeeds.opml".symlink.text =
|
||||||
feeds.feedsToOpml wanted-feeds
|
feeds.feedsToOpml wanted-feeds
|
||||||
;
|
;
|
||||||
|
@@ -1,9 +1,11 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
|
sane.programs.nheko = {
|
||||||
# not strictly necessary, but allows caching articles; offline use, etc.
|
# not strictly necessary, but allows caching articles; offline use, etc.
|
||||||
sane.programs.nheko.persist.private = [
|
persist.byStore.private = [
|
||||||
".config/nheko" # config file (including client token)
|
".config/nheko" # config file (including client token)
|
||||||
".cache/nheko" # media cache
|
".cache/nheko" # media cache
|
||||||
".local/share/nheko" # per-account state database
|
".local/share/nheko" # per-account state database
|
||||||
];
|
];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,6 @@
|
|||||||
{
|
{
|
||||||
# provides `nix-locate`, backed by the manually run `nix-index`
|
# provides `nix-locate`, backed by the manually run `nix-index`
|
||||||
sane.programs.nix-index = {
|
sane.programs.nix-index = {
|
||||||
persist.plaintext = [ ".cache/nix-index" ];
|
persist.byStore.plaintext = [ ".cache/nix-index" ];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user