Compare commits
287 Commits
staging/nu
...
staging/dn
Author | SHA1 | Date | |
---|---|---|---|
20bdad9132 | |||
7aaab9eb30 | |||
481110fefb | |||
c44f69a01f | |||
adbc2a76c3 | |||
34ed201aff | |||
4d63b81b05 | |||
e1a18cdae1 | |||
2a1d87650b | |||
4a18dfeef3 | |||
ff1aece1ed | |||
05cf5e376a | |||
855a66499f | |||
b9cc581736 | |||
0a8eee8af0 | |||
a40fc7e112 | |||
6bbb5669a6 | |||
c8d5411462 | |||
af4cfc29b1 | |||
9942025a2f | |||
04f7287781 | |||
14ae501433 | |||
46edc56a32 | |||
7907623887 | |||
c542e120ef | |||
7fcff0b6a2 | |||
32671201a4 | |||
4d2268b5f1 | |||
e5fe7c093a | |||
162f3a291c | |||
31740befbf | |||
0c610c8f1c | |||
e9dc22c1f2 | |||
75e6393680 | |||
9ca6857f4d | |||
8c30b87a94 | |||
6ffd6693cb | |||
e11fe929f4 | |||
3dcd5629a7 | |||
4cf4c38da3 | |||
e0e3c36d1b | |||
108c1d9d60 | |||
c6e16ebc13 | |||
aa60838551 | |||
d6bde02dfe | |||
d07bb03936 | |||
1ab2f42ff4 | |||
e0d20cb62a | |||
f8944c8379 | |||
ca38bb4aec | |||
287817056f | |||
5cc7ced859 | |||
4dc5378b3e | |||
fe7e440997 | |||
e4262cb0bc | |||
35c9f2bf60 | |||
13794e9eaa | |||
a33950da62 | |||
37995e23c2 | |||
66156829d9 | |||
3c40fa6982 | |||
c1ddddddc0 | |||
aae118b476 | |||
7e402ce974 | |||
5b80308074 | |||
e5c94b410f | |||
209c18cb38 | |||
616a2dd19f | |||
5b0f898c62 | |||
a541e866a1 | |||
d3eb0bee26 | |||
2ca0f6ea62 | |||
66be38bfbf | |||
4785e54024 | |||
aecb55c79e | |||
1759d5a075 | |||
f50feb6c96 | |||
d2b50b53d4 | |||
4550299bdb | |||
67a23c01e9 | |||
077d11a0d6 | |||
49db12b8fd | |||
8cde4135b1 | |||
12cd61c9f7 | |||
c882fb673e | |||
19f022a682 | |||
839f51a6a2 | |||
9b2b261bd3 | |||
b2fe449c7f | |||
da5e3e6742 | |||
68763ade1f | |||
1fadd6b177 | |||
2ac9c98bc0 | |||
67b6dfc3d3 | |||
8dddf83eb7 | |||
e41ff28746 | |||
6d1baab11f | |||
a6f68ac90c | |||
3aa1a9f674 | |||
21d17c330f | |||
ae2e4931f3 | |||
c662936490 | |||
d6fb2ab5b4 | |||
8a28e347f5 | |||
a8eaaa4e7a | |||
0289dee658 | |||
c8aa6a057f | |||
dd17843c76 | |||
ae7e372b97 | |||
af9d435517 | |||
4d92458db3 | |||
19944c2a46 | |||
3b5d08f808 | |||
c748de74a0 | |||
951e3a617a | |||
11d5af99b2 | |||
7dadea94da | |||
abfd516025 | |||
e2e044a8f4 | |||
bacc17e333 | |||
6e483c9ed9 | |||
ace9d71d0e | |||
54c7d00621 | |||
ec77cdaa49 | |||
72e8d62145 | |||
6344c3da8b | |||
88f91bd2d4 | |||
dfc4ac2f86 | |||
43ec95f54a | |||
25f4661b29 | |||
56f7011ada | |||
85c8292f9a | |||
2db988b67c | |||
6301ea3a16 | |||
1596685d2b | |||
4f494228d3 | |||
12d120cd81 | |||
dc0c3ff887 | |||
aa3515ade2 | |||
16cee4e8d4 | |||
3a942ad8a4 | |||
8973cb22b7 | |||
e0bd579e44 | |||
a96e10617f | |||
14a4d8d577 | |||
3f9261d765 | |||
9b5c39e3c5 | |||
2e9eb51893 | |||
3135b92257 | |||
7ef504d14b | |||
fa952e076a | |||
bb7462523b | |||
44d3623165 | |||
42aca2483e | |||
998647c8b5 | |||
e24e76fd8f | |||
d3a7bfda7f | |||
76d875ef3c | |||
b74c4550d5 | |||
beef453802 | |||
2da544a39e | |||
32dcfde7e9 | |||
e0c2e8c149 | |||
95635be1d5 | |||
c2682fdbf3 | |||
d43d02bff5 | |||
4e6deae299 | |||
e850bb4f5f | |||
f76c180ed6 | |||
e8efc855bb | |||
7684ae91e0 | |||
b124035281 | |||
68bc670613 | |||
3881f79af0 | |||
a47e785ae7 | |||
f8d02687b9 | |||
23e39481a4 | |||
9769aea3b3 | |||
7bbec9d6f5 | |||
f8a2691ca0 | |||
d024637edf | |||
6501f4c0d7 | |||
c85a429388 | |||
99963c1133 | |||
5c52a25ccf | |||
4a782db808 | |||
f88bbfbb81 | |||
b403348e43 | |||
51ca72da84 | |||
38d921dbc9 | |||
fb427e55e8 | |||
bf56200345 | |||
7c31407ead | |||
b39a250e22 | |||
0822ed34d7 | |||
147b1c50b2 | |||
55875816d0 | |||
e25a4bbee6 | |||
dbb9e00bed | |||
6b1c3d02c1 | |||
4a448a1bf1 | |||
452a55c5e1 | |||
d10f70aff7 | |||
38423183ee | |||
5c3be90b82 | |||
eabeef9f30 | |||
f5dcca5166 | |||
d9a23dfc1b | |||
adb2162a9c | |||
ed020b56c0 | |||
ff01155efc | |||
af42cbd575 | |||
974656a80a | |||
318efe09e2 | |||
88bce722d5 | |||
74e3aa02b9 | |||
05ee8f92b3 | |||
a5fafee4dd | |||
4afdc11882 | |||
310f5982cb | |||
b6ae9f3646 | |||
0270ccdebd | |||
efd45c58f1 | |||
596aaf93f6 | |||
943008ae5b | |||
9d6629ad12 | |||
9e21101207 | |||
5997283cef | |||
3c2715648c | |||
265642c8da | |||
d7bed3bec2 | |||
079ab08642 | |||
e34c9cc190 | |||
6ff2c8acae | |||
04e8e72ae3 | |||
5b33c85e75 | |||
083d905f4c | |||
dd25c26845 | |||
3bdbd9db7b | |||
a077009064 | |||
46baec344d | |||
a23f5c148c | |||
ada8b75670 | |||
852a3e7686 | |||
6dbcc89c2e | |||
d661a0776a | |||
e71079b354 | |||
99eb0962ad | |||
9c79791428 | |||
f09a10a168 | |||
4ddf381d41 | |||
d5ffa6d796 | |||
24364992e1 | |||
b9afd1e340 | |||
5936ea5008 | |||
c4bf887fe6 | |||
20e2ff1fe7 | |||
59a2259105 | |||
bfcbea5ca1 | |||
0376b15a2f | |||
26c3441344 | |||
51c7ccd782 | |||
74ed7bff11 | |||
836b74991a | |||
fbae81eca7 | |||
89f28e63b4 | |||
f89f136041 | |||
d220aadc2d | |||
94a8c00a40 | |||
3e94a0d0f0 | |||
094356cae2 | |||
a78c516817 | |||
e0047150cc | |||
5bf80a701d | |||
b44c0e774e | |||
6b9bbe278f | |||
afdf3442b4 | |||
b5b32fde95 | |||
96eb427ea7 | |||
8ffb7b5893 | |||
1aed894d2e | |||
13ee861b0d | |||
35d6f40263 | |||
185f1f8f11 | |||
ff081f3da6 | |||
b15fd0ced5 | |||
dc0ad98db6 |
@@ -8,7 +8,7 @@ keys:
|
||||
- &host_servo age1tzlyex2z6t88tg9h82943e39shxhmqeyr7ywhlwpdjmyqsndv3qq27x0rf
|
||||
- &host_moby age18vq5ktwgeaysucvw9t67drqmg5zd5c5k3le34yqxckkfj7wqdqgsd4ejmt
|
||||
creation_rules:
|
||||
- path_regex: secrets/universal*
|
||||
- path_regex: secrets/common*
|
||||
key_groups:
|
||||
- age:
|
||||
- *user_desko_colin
|
||||
@@ -26,19 +26,19 @@ creation_rules:
|
||||
- *user_lappy_colin
|
||||
- *user_servo_colin
|
||||
- *host_servo
|
||||
- path_regex: secrets/desko.yaml$
|
||||
- path_regex: secrets/desko*
|
||||
key_groups:
|
||||
- age:
|
||||
- *user_desko_colin
|
||||
- *user_lappy_colin
|
||||
- *host_desko
|
||||
- path_regex: secrets/lappy.yaml$
|
||||
- path_regex: secrets/lappy*
|
||||
key_groups:
|
||||
- age:
|
||||
- *user_lappy_colin
|
||||
- *user_desko_colin
|
||||
- *host_lappy
|
||||
- path_regex: secrets/moby.yaml$
|
||||
- path_regex: secrets/moby*
|
||||
key_groups:
|
||||
- age:
|
||||
- *user_desko_colin
|
||||
|
10
README.md
10
README.md
@@ -20,6 +20,8 @@ directly here; even the sources for those packages is often kept here too.
|
||||
- the bulk of config which isn't factored with external use in mind.
|
||||
- that is, if you were to add this repo to a flake.nix for your own use,
|
||||
you won't likely be depending on anything in this directory.
|
||||
- `integrations/`
|
||||
- code intended for consumption by external tools (e.g. the Nix User Repos)
|
||||
- `modules/`
|
||||
- config which is gated behind `enable` flags, in similar style to nixpkgs'
|
||||
`nixos/` directory.
|
||||
@@ -32,7 +34,7 @@ directly here; even the sources for those packages is often kept here too.
|
||||
- `pkgs/`
|
||||
- derivations for things not yet packaged in nixpkgs.
|
||||
- derivations for things from nixpkgs which i need to `override` for some reason.
|
||||
- inline code for wholly custom packages (e.g. `pkgs/sane-scripts/` for CLI tools
|
||||
- inline code for wholly custom packages (e.g. `pkgs/additional/sane-scripts/` for CLI tools
|
||||
that are highly specific to my setup).
|
||||
- `scripts/`
|
||||
- scripts which are referenced by other things in this repo.
|
||||
@@ -94,6 +96,12 @@ this should be a pretty "standard" flake. just reference it, and import either
|
||||
- `nixosModules.sane` (for the modules)
|
||||
- `overlays.pkgs` (for the packages)
|
||||
|
||||
## Mirrors
|
||||
|
||||
this repo exists in a few known locations:
|
||||
- primary: <https://git.uninsane.org/colin/nix-files>
|
||||
- mirror: <https://github.com/nix-community/nur-combined/tree/master/repos/colinsane>
|
||||
|
||||
## Contact
|
||||
|
||||
if you want to contact me for questions, or collaborate to split something useful into a shared repo, etc,
|
||||
|
75
TODO.md
Normal file
75
TODO.md
Normal file
@@ -0,0 +1,75 @@
|
||||
## BUGS
|
||||
- why i need to manually restart `wireguard-wg-ovpns` on servo periodically
|
||||
- else DNS fails
|
||||
|
||||
## REFACTORING:
|
||||
### sops/secrets
|
||||
- attach secrets to the thing they're used by (sane.programs)
|
||||
- rework secrets to leverage `sane.fs`
|
||||
- remove sops activation script as it's covered by my systemd sane.fs impl
|
||||
|
||||
### roles
|
||||
- allow any host to take the role of `uninsane.org`
|
||||
- will make it easier to test new services?
|
||||
|
||||
### upstreaming
|
||||
- split out a trust-dns module
|
||||
- see: <https://github.com/NixOS/nixpkgs/pull/205866#issuecomment-1575753054>
|
||||
- bump nodejs version in lemmy-ui
|
||||
- add updateScripts to all my packages in nixpkgs
|
||||
- fix lightdm-mobile-greeter for newer libhandy
|
||||
- port zecwallet-lite to a from-source build
|
||||
- fix or abandon Whalebird
|
||||
|
||||
|
||||
## IMPROVEMENTS:
|
||||
### security/resilience
|
||||
- validate duplicity backups!
|
||||
- encrypt more ~ dirs (~/archives, ~/records, ..?)
|
||||
- best to do this after i know for sure i have good backups
|
||||
- have `sane.programs` be wrapped such that they run in a cgroup?
|
||||
- at least, only give them access to the portion of the fs they *need*.
|
||||
- Android takes approach of giving each app its own user: could hack that in here.
|
||||
- canaries for important services
|
||||
- e.g. daily email checks; daily backup checks
|
||||
|
||||
### user experience
|
||||
- firefox/librewolf: don't show browserpass/sponsorblock/metamask "first run" on every boot
|
||||
- moby: improve gPodder launch time
|
||||
- moby: replace jellyfin-desktop with jellyfin-vue?
|
||||
- allows (maybe) to cache media for offline use
|
||||
- "newer" jellyfin client
|
||||
- not packaged for nix
|
||||
- find a nice desktop ActivityPub client
|
||||
- package Nix/NixOS docs for Zeal
|
||||
- install [doc-browser](https://github.com/qwfy/doc-browser)
|
||||
- this supports both dash (zeal) *and* the datasets from <https://devdocs.io> (which includes nix!)
|
||||
- install [devhelp](https://wiki.gnome.org/Apps/Devhelp) (gnome)
|
||||
- auto-mount servo
|
||||
- have xdg-open parse `<repo:...> URIs (or adjust them so that it _can_ parse)
|
||||
- `sane.programs`: auto-populate defaults with everything from `pkgs`
|
||||
- zsh: disable "command not found" corrections
|
||||
- sxmo: allow rotation to the upside-down position
|
||||
- see: <repo:mil/sxmo-utils:scripts/core/sxmo_autorotate.sh>
|
||||
- all orientations *except* upside down are supported
|
||||
- sxmo: launch with auto-rotation enabled
|
||||
|
||||
### perf
|
||||
- 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?
|
||||
- fix OOM for large builds like webkitgtk
|
||||
- these use significant /tmp space.
|
||||
- either place /tmp on encrypted-cleared-at-boot storage
|
||||
- which probably causes each CPU load for the encryption
|
||||
- **or set up encrypted swap**
|
||||
- encrypted swap could remove the need for my encrypted-cleared-at-boot stuff
|
||||
|
||||
|
||||
## NEW FEATURES:
|
||||
- add a FTP-accessible file share to servo
|
||||
- just /var/www?
|
||||
- migrate MAME cabinet to nix
|
||||
- boot it from PXE from servo?
|
||||
- enable IPv6
|
40
flake.lock
generated
40
flake.lock
generated
@@ -18,11 +18,11 @@
|
||||
"mobile-nixos": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1680563603,
|
||||
"narHash": "sha256-gxSci3NTlzgkAOhaC93Q4lReX/Pjd7++imD85JOAlps=",
|
||||
"lastModified": 1683422260,
|
||||
"narHash": "sha256-79zaClbubRkBNlJ04OSADILuLQHH48N5fu296hEWYlw=",
|
||||
"owner": "nixos",
|
||||
"repo": "mobile-nixos",
|
||||
"rev": "4aa0afd84005b79be4d5361b56a60df9e9bd4ea3",
|
||||
"rev": "ba4638836e94a8f16d1d1f9e8c0530b86078029c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -36,11 +36,11 @@
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1678202930,
|
||||
"narHash": "sha256-SF82/tTnagdazlETJLzXD9kjZ6lyk38agdLbmMx1UZE=",
|
||||
"lastModified": 1684319086,
|
||||
"narHash": "sha256-5wwlkWqP1cQUPXp/PJsi09FkgAule5yBghngRZZbUQg=",
|
||||
"owner": "edolstra",
|
||||
"repo": "nix-serve",
|
||||
"rev": "3b6d30016d910a43e0e16f94170440a3e0b8fa8d",
|
||||
"rev": "e6e3d09438e803daa5374ad8edf1271289348456",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -66,11 +66,11 @@
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1682173319,
|
||||
"narHash": "sha256-tPhOpJJ+wrWIusvGgIB2+x6ILfDkEgQMX0BTtM5vd/4=",
|
||||
"lastModified": 1684632198,
|
||||
"narHash": "sha256-SdxMPd0WmU9MnDBuuy7ouR++GftrThmSGL7PCQj/uVI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "ee7ec1c71adc47d2e3c2d5eb0d6b8fbbd42a8d1c",
|
||||
"rev": "d0dade110dc7072d67ce27826cfe9ab2ab0cf247",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -82,16 +82,16 @@
|
||||
},
|
||||
"nixpkgs-unpatched": {
|
||||
"locked": {
|
||||
"lastModified": 1682404149,
|
||||
"narHash": "sha256-vilYNldFXiu56HGD0lPcWsiED7EmjGMViCLZoQsv7Jk=",
|
||||
"lastModified": 1684935479,
|
||||
"narHash": "sha256-6QMMsXMr2nhmOPHdti2j3KRHt+bai2zw+LJfdCl97Mk=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d0ea36ece469a71a909ebff90777c2f7a49478bb",
|
||||
"rev": "f91ee3065de91a3531329a674a45ddcb3467a650",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "staging-next",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -113,11 +113,11 @@
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1682338428,
|
||||
"narHash": "sha256-T7AL/Us6ecxowjMAlO77GETTQO2SO+1XX2+Y/OSfHk8=",
|
||||
"lastModified": 1684637723,
|
||||
"narHash": "sha256-0vAxL7MVMhGbTkAyvzLvleELHjVsaS43p+PR1h9gzNQ=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "7c8e9727a2ecf9994d4a63d577ad5327e933b6a4",
|
||||
"rev": "4ccdfb573f323a108a44c13bb7730e42baf962a9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -134,11 +134,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1682815555,
|
||||
"narHash": "sha256-mu4axnbR6cSgnNBGrSydxmKlKWrnHLKlpNmmbqD2V9E=",
|
||||
"lastModified": 1684528780,
|
||||
"narHash": "sha256-QdYxjcTCCLPv++1v9tJBL98nn/AFx0fmzlgzcLK6KRE=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "da209f34ce34eb6b8c4d2b3256a02eb23ad9f655",
|
||||
"revCount": 191,
|
||||
"rev": "f3747a1dad3d34880613821faf26357ba432d3d7",
|
||||
"revCount": 194,
|
||||
"type": "git",
|
||||
"url": "https://git.uninsane.org/colin/uninsane"
|
||||
},
|
||||
|
60
flake.nix
60
flake.nix
@@ -43,8 +43,8 @@
|
||||
# - use `staging` if no staging-next branch has been cut.
|
||||
#
|
||||
# <https://github.com/nixos/nixpkgs/tree/nixos-unstable>
|
||||
# nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||
nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=staging-next";
|
||||
nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||
# nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=staging-next";
|
||||
# nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=staging";
|
||||
|
||||
mobile-nixos = {
|
||||
@@ -92,36 +92,28 @@
|
||||
nixpkgs = nixpkgs-unpatched;
|
||||
};
|
||||
|
||||
nixpkgsCompiledBy = local: nixpkgs.legacyPackages."${local}";
|
||||
nixpkgsCompiledBy = system: nixpkgs.legacyPackages."${system}";
|
||||
|
||||
evalHost = { name, local, target }:
|
||||
let
|
||||
# XXX: we'd prefer to use `nixosSystem = (nixpkgsCompiledBy target).nixos`
|
||||
# but it doesn't propagate config to the underlying pkgs, meaning it doesn't let you use
|
||||
# non-free packages even after setting nixpkgs.allowUnfree.
|
||||
# XXX: patch using the target -- not local -- otherwise the target will
|
||||
# need to emulate the host in order to rebuild!
|
||||
nixosSystem = import ((nixpkgsCompiledBy target).path + "/nixos/lib/eval-config.nix");
|
||||
in
|
||||
(nixosSystem {
|
||||
modules = [
|
||||
(import ./hosts/instantiate.nix { localSystem = local; hostName = name; })
|
||||
self.nixosModules.default
|
||||
self.nixosModules.passthru
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
self.overlays.disable-flakey-tests
|
||||
self.overlays.passthru
|
||||
self.overlays.pins
|
||||
self.overlays.pkgs
|
||||
# self.overlays.optimizations
|
||||
];
|
||||
nixpkgs.hostPlatform = target;
|
||||
# nixpkgs.buildPlatform = local; # set by instantiate.nix instead
|
||||
# nixpkgs.config.replaceStdenv = { pkgs }: pkgs.ccacheStdenv;
|
||||
}
|
||||
evalHost = { name, local, target }: nixpkgs.lib.nixosSystem {
|
||||
system = target;
|
||||
modules = [
|
||||
(import ./hosts/instantiate.nix { localSystem = local; hostName = name; })
|
||||
self.nixosModules.default
|
||||
self.nixosModules.passthru
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
self.overlays.passthru
|
||||
self.overlays.sane-all
|
||||
];
|
||||
});
|
||||
}
|
||||
({ lib, ... }: {
|
||||
# TODO: does the earlier `system` arg to nixosSystem make its way here?
|
||||
nixpkgs.hostPlatform.system = target;
|
||||
# nixpkgs.buildPlatform = local; # set by instantiate.nix instead
|
||||
# nixpkgs.config.replaceStdenv = { pkgs }: pkgs.ccacheStdenv;
|
||||
})
|
||||
];
|
||||
};
|
||||
in {
|
||||
nixosConfigurations =
|
||||
let
|
||||
@@ -175,11 +167,13 @@
|
||||
|
||||
# unofficial output
|
||||
host-pkgs = mapAttrValues (host: host.config.system.build.pkgs) self.nixosConfigurations;
|
||||
host-programs = mapAttrValues (host: mapAttrValues (p: p.package) host.config.sane.programs) self.nixosConfigurations;
|
||||
|
||||
overlays = {
|
||||
# N.B.: `nix flake check` requires every overlay to take `final: prev:` at defn site,
|
||||
# hence the weird redundancy.
|
||||
default = final: prev: self.overlays.pkgs final prev;
|
||||
sane-all = final: prev: import ./overlays/all.nix final prev;
|
||||
disable-flakey-tests = final: prev: import ./overlays/disable-flakey-tests.nix final prev;
|
||||
pkgs = final: prev: import ./overlays/pkgs.nix final prev;
|
||||
pins = final: prev: import ./overlays/pins.nix final prev;
|
||||
@@ -305,6 +299,12 @@
|
||||
path = ./templates/pkgs/rust;
|
||||
description = "rust package fit to ship in nixpkgs";
|
||||
};
|
||||
pkgs.make = {
|
||||
# initialize with:
|
||||
# - `nix flake init -t '/home/colin/dev/nixos/#pkgs.make'`
|
||||
path = ./templates/pkgs/make;
|
||||
description = "default Makefile-based derivation";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
7
hosts/README.md
Normal file
7
hosts/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## directory structure
|
||||
- by-name/<hostname>: configuration which is evaluated _only_ for the given hostname
|
||||
- common/: configuration which applies to all hosts
|
||||
- modules/: nixpkgs-style modules which may be used by multiple hosts, but configured separately per host.
|
||||
- ideally no module here has effect unless `enable`d
|
||||
- however, `enable` may default to true
|
||||
- and in practice some of these modules surely aren't fully "disableable"
|
@@ -4,12 +4,16 @@
|
||||
./fs.nix
|
||||
];
|
||||
|
||||
sops.secrets.colin-passwd.neededForUsers = true;
|
||||
|
||||
sane.roles.build-machine.enable = true;
|
||||
sane.roles.ac = true;
|
||||
sane.roles.client = true;
|
||||
sane.roles.dev-machine = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."desko".wg-home.ip;
|
||||
sane.services.duplicity.enable = true;
|
||||
sane.services.nixserve.sopsFile = ../../../secrets/desko.yaml;
|
||||
sane.services.nixserve.secretKeyFile = config.sops.secrets.nix_serve_privkey.path;
|
||||
|
||||
sane.gui.sway.enable = true;
|
||||
sane.programs.iphoneUtils.enableFor.user.colin = true;
|
||||
@@ -22,11 +26,6 @@
|
||||
# needed to use libimobiledevice/ifuse, for iphone sync
|
||||
services.usbmuxd.enable = true;
|
||||
|
||||
sops.secrets.colin-passwd = {
|
||||
sopsFile = ../../../secrets/desko.yaml;
|
||||
neededForUsers = true;
|
||||
};
|
||||
|
||||
# don't enable wifi by default: it messes with connectivity.
|
||||
systemd.services.iwd.enable = false;
|
||||
|
||||
@@ -37,15 +36,9 @@
|
||||
services.snapper.configs.nix = {
|
||||
# TODO: for the impermanent setup, we'd prefer to just do /nix/persist,
|
||||
# but that also requires setting up the persist dir as a subvol
|
||||
subvolume = "/nix";
|
||||
SUBVOLUME = "/nix";
|
||||
# TODO: ALLOW_USERS doesn't seem to work. still need `sudo snapper -c nix list`
|
||||
extraConfig = ''
|
||||
ALLOW_USERS = "colin";
|
||||
'';
|
||||
};
|
||||
|
||||
sops.secrets.duplicity_passphrase = {
|
||||
sopsFile = ../../../secrets/desko.yaml;
|
||||
ALLOW_USERS = [ "colin" ];
|
||||
};
|
||||
|
||||
programs.steam = {
|
||||
|
@@ -2,11 +2,11 @@
|
||||
{
|
||||
imports = [
|
||||
./fs.nix
|
||||
./polyfill.nix
|
||||
];
|
||||
|
||||
sane.yggdrasil.enable = true;
|
||||
|
||||
sane.roles.client = true;
|
||||
sane.roles.dev-machine = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."lappy".wg-home.ip;
|
||||
|
||||
@@ -19,12 +19,8 @@
|
||||
"desktopGuiApps"
|
||||
"stepmania"
|
||||
];
|
||||
sane.programs.mx-sanebot.enableFor.system = true; # for the docs
|
||||
|
||||
sops.secrets.colin-passwd = {
|
||||
sopsFile = ../../../secrets/lappy.yaml;
|
||||
neededForUsers = true;
|
||||
};
|
||||
sops.secrets.colin-passwd.neededForUsers = true;
|
||||
|
||||
# default config: https://man.archlinux.org/man/snapper-configs.5
|
||||
# defaults to something like:
|
||||
@@ -33,7 +29,8 @@
|
||||
services.snapper.configs.nix = {
|
||||
# TODO: for the impermanent setup, we'd prefer to just do /nix/persist,
|
||||
# but that also requires setting up the persist dir as a subvol
|
||||
subvolume = "/nix";
|
||||
SUBVOLUME = "/nix";
|
||||
ALLOW_USERS = [ "colin" ];
|
||||
};
|
||||
|
||||
# TODO: only here for debugging
|
||||
|
32
hosts/by-name/lappy/polyfill.nix
Normal file
32
hosts/by-name/lappy/polyfill.nix
Normal file
@@ -0,0 +1,32 @@
|
||||
# doesn't actually *enable* anything,
|
||||
# but sets up any modules such that if they *were* enabled, they'll act as expected.
|
||||
{ ... }:
|
||||
{
|
||||
sane.gui.sxmo = {
|
||||
greeter = "sway";
|
||||
settings = {
|
||||
# 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";
|
||||
# these identifiers are from `swaymsg -t get_inputs`
|
||||
SXMO_VOLUME_BUTTON = "1:1:AT_Translated_Set_2_keyboard";
|
||||
# SXMO_VOLUME_BUTTON = "none";
|
||||
SXMO_POWER_BUTTON = "0:1:Power_Button";
|
||||
# SXMO_POWER_BUTTON = "none";
|
||||
SXMO_DISABLE_LEDS = "1";
|
||||
SXMO_UNLOCK_IDLE_TIME = "120"; # default
|
||||
# sxmo tries to determine device type from /proc/device-tree/compatible,
|
||||
# but that doesn't seem to exist on NixOS? (or maybe it just doesn't exist
|
||||
# on non-aarch64 builds).
|
||||
# the device type informs (at least):
|
||||
# - SXMO_WIFI_MODULE
|
||||
# - SXMO_RTW_SCAN_INTERVAL
|
||||
# - SXMO_SYS_FILES
|
||||
# - SXMO_TOUCHSCREEN_ID
|
||||
# - SXMO_MONITOR
|
||||
# - SXMO_ALSA_CONTROL_NAME
|
||||
# - SXMO_SWAY_SCALE
|
||||
# see <repo:mil/sxmo-utils:scripts/deviceprofiles>
|
||||
# SXMO_DEVICE_NAME = "pine64,pinephone-1.2";
|
||||
};
|
||||
};
|
||||
}
|
@@ -4,6 +4,7 @@
|
||||
./firmware.nix
|
||||
./fs.nix
|
||||
./kernel.nix
|
||||
./polyfill.nix
|
||||
];
|
||||
|
||||
sane.roles.client = true;
|
||||
@@ -15,12 +16,9 @@
|
||||
users.users.colin.initialPassword = "147147";
|
||||
services.getty.autologinUser = "root"; # allows for emergency maintenance?
|
||||
|
||||
sops.secrets.colin-passwd = {
|
||||
sopsFile = ../../../secrets/moby.yaml;
|
||||
neededForUsers = true;
|
||||
};
|
||||
sops.secrets.colin-passwd.neededForUsers = true;
|
||||
|
||||
sane.web-browser = {
|
||||
sane.programs.web-browser.config = {
|
||||
# compromise impermanence for the sake of usability
|
||||
persistCache = "private";
|
||||
persistData = "private";
|
||||
@@ -31,6 +29,7 @@
|
||||
};
|
||||
|
||||
sane.user.persist.plaintext = [
|
||||
# TODO: make this just generally conditional upon pulse being enabled?
|
||||
".config/pulse" # persist pulseaudio volume
|
||||
];
|
||||
|
||||
@@ -78,14 +77,30 @@
|
||||
# enable rotation sensor
|
||||
hardware.sensor.iio.enable = true;
|
||||
|
||||
# from https://gitlab.manjaro.org/manjaro-arm/packages/community/phosh/alsa-ucm-pinephone
|
||||
# mobile-nixos does this same thing, with *slightly different settings*.
|
||||
# i trust manjaro more because the guy maintaining that is actively trying to upstream into alsa-ucm-conf.
|
||||
# an alternative may be to build a custom alsa with the PinePhone config patch applied:
|
||||
# - <https://github.com/alsa-project/alsa-ucm-conf/pull/134>
|
||||
# that would make this be not device-specific
|
||||
environment.variables.ALSA_CONFIG_UCM2 = "${./ucm2}";
|
||||
systemd.services.pulseaudio.environment.ALSA_CONFIG_UCM2 = "${./ucm2}";
|
||||
# inject specialized alsa configs via the environment.
|
||||
# specifically, this gets the pinephone headphones & internal earpiece working.
|
||||
# see pkgs/patched/alsa-ucm-conf for more info.
|
||||
environment.variables.ALSA_CONFIG_UCM2 = "/run/current-system/sw/share/alsa/ucm2";
|
||||
environment.pathsToLink = [ "/share/alsa/ucm2" ];
|
||||
environment.systemPackages = [ pkgs.alsa-ucm-conf-sane ];
|
||||
systemd =
|
||||
let ucm-env = config.environment.variables.ALSA_CONFIG_UCM2;
|
||||
in {
|
||||
# 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
|
||||
user.services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
user.services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
user.services.wireplumber.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
services.wireplumber.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
};
|
||||
|
||||
|
||||
hardware.opengl.driSupport = true;
|
||||
}
|
||||
|
@@ -44,68 +44,6 @@ let
|
||||
"sha256-6ywm3dQQ5JYl60CLKarxlSUukwi4QzqctCj3tVgzFbo="
|
||||
)
|
||||
];
|
||||
|
||||
# pinephone uses the linux dtb at arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
|
||||
# - this includes sun50i-a64.dtsi
|
||||
# - and sun50i-a64-cpu-opp.dtsi
|
||||
# - no need to touch the allwinner-h6 stuff: that's the SBC pine product
|
||||
# - i think it's safe to ignore sun9i stuff, but i don't know what it is
|
||||
kernelConfig = with lib.kernel; {
|
||||
# NB: nix adds the CONFIG_ prefix to each of these.
|
||||
# if you add the prefix yourself nix will IGNORE YOUR CONFIG.
|
||||
RTL8723CS = module;
|
||||
BT_HCIUART_3WIRE = yes;
|
||||
BT_HCIUART_RTL = yes;
|
||||
RTL8XXXU_UNTESTED = yes;
|
||||
BT_BNEP_MC_FILTER = yes;
|
||||
BT_BNEP_PROTO_FILTER = yes;
|
||||
BT_HS = yes;
|
||||
BT_LE = yes;
|
||||
# relevant configs inherited from nixos defaults (or above additions):
|
||||
# CONFIG_BT=m
|
||||
# CONFIG_BT_BREDR=y
|
||||
# CONFIG_BT_RFCOMM=m
|
||||
# CONFIG_BT_RFCOMM_TTY=y
|
||||
# CONFIG_BT_BNEP=m
|
||||
# CONFIG_BT_HIDP=m
|
||||
# CONFIG_BT_RTL=m
|
||||
# CONFIG_BT_HCIBTUSB=m
|
||||
# CONFIG_BT_HCIBTUSB_BCM=y
|
||||
# CONFIG_BT_HCIBTUSB_RTL=y
|
||||
# CONFIG_BT_HCIUART=m
|
||||
# CONFIG_BT_HCIUART_SERDEV=y
|
||||
# CONFIG_BT_HCIUART_H4=y
|
||||
# CONFIG_BT_HCIUART_LL=y
|
||||
# CONFIG_RTL_CARDS=m
|
||||
# CONFIG_RTLWIFI=m
|
||||
# CONFIG_RTLWIFI_PCI=m
|
||||
# CONFIG_RTLWIFI_USB=m
|
||||
# CONFIG_RTLWIFI_DEBUG=y
|
||||
# CONFIG_RTL8723_COMMON=m
|
||||
# CONFIG_RTLBTCOEXIST=m
|
||||
# CONFIG_RTL8XXXU=m
|
||||
# CONFIG_RTLLIB=m
|
||||
# consider adding (from mobile-nixos):
|
||||
# maybe: CONFIG_BT_HCIUART_3WIRE=y
|
||||
# maybe: CONFIG_BT_HCIUART_RTL=y
|
||||
# maybe: CONFIG_RTL8XXXU_UNTESTED=y
|
||||
# consider adding (from manjaro):
|
||||
# CONFIG_BT_6LOWPAN=m (not listed as option in nixos kernel)
|
||||
# these are referenced in the rtl8723 source, but not known to config (and not in mobile-nixos config
|
||||
# maybe: CONFIG_RTL_ODM_WLAN_DRIVER
|
||||
# maybe: CONFIG_RTL_TRIBAND_SUPPORT
|
||||
# maybe: CONFIG_SDIO_HCI
|
||||
# maybe: CONFIG_USB_HCI
|
||||
};
|
||||
|
||||
# create a kernelPatch which overrides nixos' defconfig with extra options
|
||||
patchDefconfig = config: {
|
||||
# defconfig options. this method comes from here:
|
||||
# - https://discourse.nixos.org/t/the-correct-way-to-override-the-latest-kernel-config/533/9
|
||||
name = "sane-moby-defconfig";
|
||||
patch = null;
|
||||
extraStructuredConfig = config;
|
||||
};
|
||||
in
|
||||
{
|
||||
# use Megi's kernel:
|
||||
@@ -116,22 +54,6 @@ in
|
||||
# - phosh greeter may not appear after wake from sleep
|
||||
boot.kernelPackages = pkgs.linuxPackagesFor pkgs.linux-megous;
|
||||
|
||||
boot.kernelPatches = [
|
||||
(patchDefconfig (kernelConfig //
|
||||
(with lib.kernel; {
|
||||
# disabling the sun5i_eink driver avoids this compilation error:
|
||||
# CC [M] drivers/video/fbdev/sun5i-eink-neon.o
|
||||
# aarch64-unknown-linux-gnu-gcc: error: unrecognized command line option '-mfloat-abi=softfp'
|
||||
# aarch64-unknown-linux-gnu-gcc: error: unrecognized command line option '-mfpu=neon'
|
||||
# make[3]: *** [../scripts/Makefile.build:289: drivers/video/fbdev/sun5i-eink-neon.o] Error 1
|
||||
FB_SUN5I_EINK = no;
|
||||
# used by the pinephone pro, but fails to compile with:
|
||||
# ../drivers/media/i2c/ov8858.c:1834:27: error: implicit declaration of function 'compat_ptr'
|
||||
VIDEO_OV8858 = no;
|
||||
})
|
||||
))
|
||||
];
|
||||
|
||||
# alternatively, use nixos' kernel and add the stuff we want:
|
||||
# # cross-compilation optimization:
|
||||
# boot.kernelPackages =
|
||||
@@ -143,4 +65,19 @@ in
|
||||
# boot.kernelPatches = manjaroPatches ++ [
|
||||
# (patchDefconfig kernelConfig)
|
||||
# ];
|
||||
|
||||
nixpkgs.hostPlatform.linux-kernel = {
|
||||
# defaults:
|
||||
name = "aarch64-multiplatform";
|
||||
baseConfig = "defconfig";
|
||||
DTB = true;
|
||||
autoModules = true;
|
||||
preferBuiltin = true;
|
||||
# extraConfig = ...
|
||||
# ^-- raspberry pi stuff: we don't need it.
|
||||
|
||||
# target = "Image"; # <-- default
|
||||
target = "Image.gz"; # <-- compress the kernel image
|
||||
# target = "zImage"; # <-- confuses other parts of nixos :-(
|
||||
};
|
||||
}
|
||||
|
23
hosts/by-name/moby/polyfill.nix
Normal file
23
hosts/by-name/moby/polyfill.nix
Normal file
@@ -0,0 +1,23 @@
|
||||
{ sane-lib, ... }:
|
||||
{
|
||||
sane.gui.sxmo = {
|
||||
settings = {
|
||||
# touch screen
|
||||
SXMO_LISGD_INPUT_DEVICE = "/dev/input/by-path/platform-1c2ac00.i2c-event";
|
||||
# vol and power are detected correctly by upstream
|
||||
};
|
||||
};
|
||||
# TODO: only populate this if sxmo is enabled?
|
||||
sane.user.fs.".config/sxmo/profile" = sane-lib.fs.wantedText ''
|
||||
# sourced by sxmo_init.sh
|
||||
. sxmo_common.sh
|
||||
|
||||
export SXMO_SWAY_SCALE=1.5
|
||||
export SXMO_ROTATION_GRAVITY=12800
|
||||
|
||||
export DEFAULT_COUNTRY=US
|
||||
export BROWSER=librewolf
|
||||
|
||||
export SXMO_BG_IMG="$(xdg_data_path sxmo/background.jpg)"
|
||||
'';
|
||||
}
|
@@ -4,7 +4,6 @@
|
||||
imports = [
|
||||
./fs.nix
|
||||
./net.nix
|
||||
./secrets.nix
|
||||
./services
|
||||
];
|
||||
|
||||
@@ -15,11 +14,13 @@
|
||||
signaldctl.enableFor.user.colin = true;
|
||||
};
|
||||
|
||||
sane.roles.ac = true;
|
||||
sane.roles.build-machine.enable = true;
|
||||
sane.roles.build-machine.emulation = false;
|
||||
sane.zsh.showDeadlines = false; # ~/knowledge doesn't always exist
|
||||
sane.services.dyn-dns.enable = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.enableWan = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."servo".wg-home.ip;
|
||||
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade
|
||||
|
||||
|
@@ -3,6 +3,12 @@
|
||||
{
|
||||
networking.domain = "uninsane.org";
|
||||
|
||||
sane.ports.openFirewall = true;
|
||||
sane.ports.openUpnp = true;
|
||||
|
||||
# view refused packets with: `sudo journalctl -k`
|
||||
# networking.firewall.logRefusedPackets = true;
|
||||
|
||||
# The global useDHCP flag is deprecated, therefore explicitly set to false here.
|
||||
# Per-interface useDHCP will be mandatory in the future, so this generated config
|
||||
# replicates the default behaviour.
|
||||
@@ -11,9 +17,6 @@
|
||||
# XXX colin: probably don't need this. wlan0 won't be populated unless i touch a value in networking.interfaces.wlan0
|
||||
networking.wireless.enable = false;
|
||||
|
||||
# networking.firewall.enable = false;
|
||||
networking.firewall.enable = true;
|
||||
|
||||
# this is needed to forward packets from the VPN to the host
|
||||
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
|
||||
|
||||
@@ -33,6 +36,14 @@
|
||||
# - getent ahostsv4 www.google.com
|
||||
# - try fix: <https://serverfault.com/questions/765989/connect-to-3rd-party-vpn-server-but-dont-use-it-as-the-default-route/766290#766290>
|
||||
services.resolved.enable = true;
|
||||
# without DNSSEC:
|
||||
# - dig matrix.org => works
|
||||
# - curl https://matrix.org => works
|
||||
# with default DNSSEC:
|
||||
# - dig matrix.org => works
|
||||
# - curl https://matrix.org => fails
|
||||
# i don't know why. this might somehow be interfering with the DNS run on this device (trust-dns)
|
||||
services.resolved.dnssec = "false";
|
||||
networking.nameservers = [
|
||||
# use systemd-resolved resolver
|
||||
# full resolver (which understands /etc/hosts) lives on 127.0.0.53
|
||||
@@ -145,9 +156,9 @@
|
||||
|
||||
# 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}:53
|
||||
-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}:53
|
||||
-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
|
||||
# - alternatively, we could fix DNS servers like 1.1.1.1.
|
||||
|
@@ -1,42 +0,0 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
sops.secrets."ddns_afraid" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
sops.secrets."ddns_he" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."dovecot_passwd" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."duplicity_passphrase" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."freshrss_passwd" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."matrix_synapse_secrets" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
sops.secrets."mautrix_signal_env" = {
|
||||
sopsFile = ../../../secrets/servo/mautrix_signal_env.bin;
|
||||
format = "binary";
|
||||
};
|
||||
|
||||
sops.secrets."mediawiki_pw" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."pleroma_secrets" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."wg_ovpns_privkey" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
}
|
@@ -6,7 +6,7 @@ lib.mkIf false
|
||||
systemd.services.ddns-afraid = {
|
||||
description = "update dynamic DNS entries for freedns.afraid.org";
|
||||
serviceConfig = {
|
||||
EnvironmentFile = config.sops.secrets.ddns_afraid.path;
|
||||
EnvironmentFile = config.sops.secrets."ddns_afraid.env".path;
|
||||
# TODO: ProtectSystem = "strict";
|
||||
# TODO: ProtectHome = "full";
|
||||
# TODO: PrivateTmp = true;
|
||||
|
@@ -6,7 +6,7 @@ lib.mkIf false
|
||||
systemd.services.ddns-he = {
|
||||
description = "update dynamic DNS entries for HurricaneElectric";
|
||||
serviceConfig = {
|
||||
EnvironmentFile = config.sops.secrets.ddns_he.path;
|
||||
EnvironmentFile = config.sops.secrets."ddns_he.env".path;
|
||||
# TODO: ProtectSystem = "strict";
|
||||
# TODO: ProtectHome = "full";
|
||||
# TODO: PrivateTmp = true;
|
||||
|
@@ -19,6 +19,7 @@
|
||||
./navidrome.nix
|
||||
./nixserve.nix
|
||||
./nginx.nix
|
||||
./pict-rs.nix
|
||||
./pleroma.nix
|
||||
./postgres.nix
|
||||
./prosody.nix
|
||||
|
@@ -22,20 +22,60 @@
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "ejabberd"; group = "ejabberd"; directory = "/var/lib/ejabberd"; }
|
||||
];
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
3478 # STUN/TURN
|
||||
5222 # XMPP client -> server
|
||||
5223 # XMPPS client -> server (XMPP over TLS)
|
||||
5269 # XMPP server -> server
|
||||
5270 # XMPPS server -> server (XMPP over TLS)
|
||||
5280 # bosh
|
||||
5281 # bosh (https) ??
|
||||
5349 # STUN/TURN (TLS)
|
||||
5443 # web services (file uploads, websockets, admin)
|
||||
];
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
3478 # STUN/TURN
|
||||
];
|
||||
sane.ports.ports."3478" = {
|
||||
protocol = [ "tcp" "udp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-stun-turn";
|
||||
};
|
||||
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-bosh-https";
|
||||
};
|
||||
sane.ports.ports."5349" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-stun-turn-over-tls";
|
||||
};
|
||||
sane.ports.ports."5443" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-web-services"; # file uploads, websockets, admin
|
||||
};
|
||||
|
||||
# TODO: forward these TURN ports!
|
||||
networking.firewall.allowedTCPPortRanges = [{
|
||||
from = 49152; # TURN
|
||||
to = 49408;
|
||||
@@ -77,7 +117,7 @@
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet = {
|
||||
# XXX: SRV records have to point to something with a A/AAAA record; no CNAMEs
|
||||
A."xmpp" = "%NATIVE%";
|
||||
A."xmpp" = "%ANATIVE%";
|
||||
CNAME."muc.xmpp" = "xmpp";
|
||||
CNAME."pubsub.xmpp" = "xmpp";
|
||||
CNAME."upload.xmpp" = "xmpp";
|
||||
@@ -234,7 +274,7 @@
|
||||
use_turn: true
|
||||
turn_min_port: 49152
|
||||
turn_max_port: 65535
|
||||
turn_ipv4_address: %NATIVE%
|
||||
turn_ipv4_address: %ANATIVE%
|
||||
-
|
||||
# STUN+TURN UDP
|
||||
port: 3478
|
||||
@@ -243,7 +283,7 @@
|
||||
use_turn: true
|
||||
turn_min_port: 49152
|
||||
turn_max_port: 65535
|
||||
turn_ipv4_address: %NATIVE%
|
||||
turn_ipv4_address: %ANATIVE%
|
||||
-
|
||||
# STUN+TURN TLS over TCP
|
||||
port: 5349
|
||||
@@ -254,7 +294,7 @@
|
||||
use_turn: true
|
||||
turn_min_port: 49152
|
||||
turn_max_port: 65535
|
||||
turn_ipv4_address: %NATIVE%
|
||||
turn_ipv4_address: %ANATIVE%
|
||||
|
||||
# TODO: enable mod_fail2ban
|
||||
# TODO(low): look into mod_http_fileserver for serving macros?
|
||||
@@ -387,7 +427,7 @@
|
||||
# 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?
|
||||
rm -f /var/lib/ejabberd/ejabberd.yaml.new
|
||||
${sed} "s/%NATIVE%/$ip/" ${config-in} > /var/lib/ejabberd/ejabberd.yaml.new
|
||||
${sed} "s/%ANATIVE%/$ip/" ${config-in} > /var/lib/ejabberd/ejabberd.yaml.new
|
||||
mv /var/lib/ejabberd/ejabberd.yaml{.new,}
|
||||
'';
|
||||
|
||||
|
@@ -6,11 +6,18 @@
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
# exposed over non-vpn imap.uninsane.org
|
||||
143 # IMAP
|
||||
993 # IMAPS
|
||||
];
|
||||
sane.ports.ports."143" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-imap-imap.uninsane.org";
|
||||
};
|
||||
sane.ports.ports."993" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-imaps-imap.uninsane.org";
|
||||
};
|
||||
|
||||
# exists only to manage certs for dovecot
|
||||
services.nginx.virtualHosts."imap.uninsane.org" = {
|
||||
|
@@ -28,12 +28,21 @@ in
|
||||
# "/var/lib/dovecot"
|
||||
];
|
||||
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
# exposed over vpn mx.uninsane.org
|
||||
25 # SMTP
|
||||
465 # SMTPS
|
||||
587 # SMTPS/submission
|
||||
];
|
||||
sane.ports.ports."25" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.ovpn = true;
|
||||
description = "colin-smtp-mx.uninsane.org";
|
||||
};
|
||||
sane.ports.ports."465" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.ovpn = true;
|
||||
description = "colin-smtps-mx.uninsane.org";
|
||||
};
|
||||
sane.ports.ports."587" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.ovpn = true;
|
||||
description = "colin-smtps-submission-mx.uninsane.org";
|
||||
};
|
||||
|
||||
# exists only to manage certs for Postfix
|
||||
services.nginx.virtualHosts."mx.uninsane.org" = {
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# config options: <https://docs.gitea.io/en-us/administration/config-cheat-sheet/>
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
@@ -10,9 +11,6 @@
|
||||
services.gitea.database.type = "postgres";
|
||||
services.gitea.database.user = "git";
|
||||
services.gitea.appName = "Perfectly Sane Git";
|
||||
services.gitea.domain = "git.uninsane.org";
|
||||
services.gitea.rootUrl = "https://git.uninsane.org/";
|
||||
services.gitea.settings.session.COOKIE_SECURE = true;
|
||||
# services.gitea.disableRegistration = true;
|
||||
|
||||
# gitea doesn't create the git user
|
||||
@@ -27,9 +25,13 @@
|
||||
};
|
||||
|
||||
services.gitea.settings = {
|
||||
# options: "Trace", "Debug", "Info", "Warn", "Error", "Critical"
|
||||
log.LEVEL = "Warn";
|
||||
server = {
|
||||
# options: "home", "explore", "organizations", "login" or URL fragment (or full URL)
|
||||
LANDING_PAGE = "explore";
|
||||
DOMAIN = "git.uninsane.org";
|
||||
ROOT_URL = "https://git.uninsane.org/";
|
||||
};
|
||||
service = {
|
||||
# timeout for email approval. 5760 = 4 days
|
||||
@@ -44,6 +46,7 @@
|
||||
ENABLE_CAPTCHA = true;
|
||||
NOREPLY_ADDRESS = "noreply.anonymous.git@uninsane.org";
|
||||
};
|
||||
session.COOKIE_SECURE = true;
|
||||
repository = {
|
||||
DEFAULT_BRANCH = "master";
|
||||
};
|
||||
@@ -58,6 +61,8 @@
|
||||
};
|
||||
#"ui.meta" = ... to customize html author/description/etc
|
||||
mailer = {
|
||||
# alternative is to use nixos-level config:
|
||||
# services.gitea.mailerPasswordFile = ...
|
||||
ENABLED = true;
|
||||
MAILER_TYPE = "sendmail";
|
||||
FROM = "notify.git@uninsane.org";
|
||||
@@ -69,8 +74,6 @@
|
||||
FORMAT = "RFC3339";
|
||||
};
|
||||
};
|
||||
# options: "Trace", "Debug", "Info", "Warn", "Error", "Critical"
|
||||
services.gitea.settings.log.LEVEL = "Warn";
|
||||
|
||||
systemd.services.gitea.serviceConfig = {
|
||||
# nix default is AF_UNIX AF_INET AF_INET6.
|
||||
@@ -96,4 +99,11 @@
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."git" = "native";
|
||||
|
||||
sane.ports.ports."22" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-git@git.uninsane.org";
|
||||
};
|
||||
}
|
||||
|
@@ -16,17 +16,30 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
# identical to:
|
||||
# services.jellyfin.openFirewall = true;
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
# https://jellyfin.org/docs/general/networking/index.html
|
||||
1900 # UPnP service discovery
|
||||
7359 # Jellyfin-specific (?) client discovery
|
||||
];
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
8096 # HTTP (for the LAN)
|
||||
8920 # HTTPS (for the LAN)
|
||||
];
|
||||
# https://jellyfin.org/docs/general/networking/index.html
|
||||
sane.ports.ports."1900" = {
|
||||
protocol = [ "udp" ];
|
||||
visibleTo.lan = true;
|
||||
description = "colin-upnp-for-jellyfin";
|
||||
};
|
||||
sane.ports.ports."7359" = {
|
||||
protocol = [ "udp" ];
|
||||
visibleTo.lan = true;
|
||||
description = "colin-jellyfin-specific-client-discovery";
|
||||
# ^ not sure if this is necessary: copied this port from nixos jellyfin.openFirewall
|
||||
};
|
||||
# not sure if 8096/8920 get used either:
|
||||
sane.ports.ports."8096" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
description = "colin-jellyfin-http-lan";
|
||||
};
|
||||
sane.ports.ports."8920" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
description = "colin-jellyfin-https-lan";
|
||||
};
|
||||
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; directory = "/var/lib/jellyfin"; }
|
||||
];
|
||||
|
@@ -1,3 +1,8 @@
|
||||
# docs:
|
||||
# - <repo:LemmyNet/lemmy:docker/federation/nginx.conf>
|
||||
# - <repo:LemmyNet/lemmy:docker/nginx.conf>
|
||||
# - <repo:LemmyNet/lemmy-ansible:templates/nginx.conf>
|
||||
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
inherit (builtins) toString;
|
||||
@@ -9,11 +14,13 @@ in {
|
||||
services.lemmy = {
|
||||
enable = true;
|
||||
settings.hostname = "lemmy.uninsane.org";
|
||||
settings.options.federation.enabled = true;
|
||||
settings.options.port = backendPort;
|
||||
# settings.database.host = "localhost";
|
||||
settings.federation.enabled = true;
|
||||
# federation.debug forces outbound federation queries to be run synchronously
|
||||
# settings.federation.debug = true;
|
||||
settings.port = backendPort;
|
||||
ui.port = uiPort;
|
||||
database.createLocally = true;
|
||||
nginx.enable = true;
|
||||
};
|
||||
|
||||
systemd.services.lemmy.serviceConfig = {
|
||||
@@ -21,7 +28,20 @@ in {
|
||||
DynamicUser = mkForce false;
|
||||
User = "lemmy";
|
||||
Group = "lemmy";
|
||||
Environment = [ "RUST_BACKTRACE=full" ];
|
||||
};
|
||||
systemd.services.lemmy.environment = {
|
||||
RUST_BACKTRACE = "full";
|
||||
# RUST_LOG = "debug";
|
||||
# upstream defaults LEMMY_DATABASE_URL = "postgres:///lemmy?host=/run/postgresql";
|
||||
# - Postgres complains that we didn't specify a user
|
||||
# lemmy formats the url as:
|
||||
# - postgres://{user}:{password}@{host}:{port}/{database}
|
||||
# SO suggests (https://stackoverflow.com/questions/3582552/what-is-the-format-for-the-postgresql-connection-string-url):
|
||||
# - postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]
|
||||
# LEMMY_DATABASE_URL = "postgres://lemmy@/run/postgresql"; # connection to server on socket "/run/postgresql/.s.PGSQL.5432" failed: FATAL: database "run/postgresql" does not exist
|
||||
# LEMMY_DATABASE_URL = "postgres://lemmy?host=/run/postgresql"; # no PostgreSQL user name specified in startup packet
|
||||
# LEMMY_DATABASE_URL = mkForce "postgres://lemmy@?host=/run/postgresql"; # WORKS
|
||||
LEMMY_DATABASE_URL = mkForce "postgres://lemmy@/lemmy?host=/run/postgresql";
|
||||
};
|
||||
users.groups.lemmy = {};
|
||||
users.users.lemmy = {
|
||||
@@ -32,28 +52,6 @@ in {
|
||||
services.nginx.virtualHosts."lemmy.uninsane.org" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
locations = let
|
||||
ui = "http://127.0.0.1:${toString uiPort}";
|
||||
backend = "http://127.0.0.1:${toString backendPort}";
|
||||
in {
|
||||
# see <LemmyNet/lemmy:docker/federation/nginx.conf>
|
||||
"~ ^/(api|pictrs|feeds|nodeinfo|.well-known)" = {
|
||||
extraConfig = ''
|
||||
set $proxpass ${ui};
|
||||
if ($http_accept = "application/activity+json") {
|
||||
set $proxpass ${backend};
|
||||
}
|
||||
if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
|
||||
set $proxpass ${backend};
|
||||
}
|
||||
|
||||
# Cuts off the trailing slash on URLs to make them valid
|
||||
rewrite ^(.+)/+$ $1 permanent;
|
||||
'';
|
||||
proxyPass = "$proxpass";
|
||||
};
|
||||
"/".proxyPass = ui;
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."lemmy" = "native";
|
||||
|
@@ -1,5 +1,6 @@
|
||||
# docs: https://nixos.wiki/wiki/Matrix
|
||||
# docs: https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-synapse
|
||||
# docs: <https://nixos.wiki/wiki/Matrix>
|
||||
# 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>
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
@@ -41,11 +42,14 @@
|
||||
}
|
||||
];
|
||||
|
||||
services.matrix-synapse.settings.x_forwarded = true; # because we proxy matrix behind nginx
|
||||
services.matrix-synapse.settings.max_upload_size = "100M"; # default is "50M"
|
||||
|
||||
services.matrix-synapse.settings.admin_contact = "admin.matrix@uninsane.org";
|
||||
services.matrix-synapse.settings.registrations_require_3pid = [ "email" ];
|
||||
|
||||
services.matrix-synapse.extraConfigFiles = [
|
||||
config.sops.secrets.matrix_synapse_secrets.path
|
||||
config.sops.secrets."matrix_synapse_secrets.yaml".path
|
||||
];
|
||||
|
||||
# services.matrix-synapse.extraConfigFiles = [builtins.toFile "matrix-synapse-extra-config" ''
|
||||
@@ -94,6 +98,10 @@
|
||||
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:8008";
|
||||
extraConfig = ''
|
||||
# allow uploading large files (matrix enforces a separate limit, downstream)
|
||||
client_max_body_size 512m;
|
||||
'';
|
||||
};
|
||||
# redirect browsers to the web client.
|
||||
# i don't think native matrix clients ever fetch the root.
|
||||
@@ -130,7 +138,7 @@
|
||||
};
|
||||
|
||||
|
||||
sops.secrets."matrix_synapse_secrets" = {
|
||||
sops.secrets."matrix_synapse_secrets.yaml" = {
|
||||
owner = config.users.users.matrix-synapse.name;
|
||||
};
|
||||
}
|
||||
|
@@ -1,4 +1,9 @@
|
||||
{ lib, ... }:
|
||||
|
||||
# XXX mx-discord-puppet uses nodejs_14 which is EOL
|
||||
# - mx-discord-puppet is abandoned upstream _and_ in nixpkgs
|
||||
# - recommended to use mautrix-discord: <https://github.com/NixOS/nixpkgs/pull/200462>
|
||||
lib.mkIf false
|
||||
{
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/mx-puppet-discord"; }
|
||||
|
@@ -112,7 +112,6 @@ in
|
||||
"/var/lib/matrix-appservice-irc/registration.yml" # auto-created by irc appservice
|
||||
];
|
||||
|
||||
# Rizon supports CertFP for auth: https://wiki.rizon.net/index.php?title=CertFP
|
||||
services.matrix-appservice-irc.enable = true;
|
||||
services.matrix-appservice-irc.registrationUrl = "http://127.0.0.1:8009";
|
||||
services.matrix-appservice-irc.settings = {
|
||||
@@ -127,12 +126,30 @@ in
|
||||
|
||||
ircService = {
|
||||
servers = {
|
||||
"irc.rizon.net" = ircServer { name = "Rizon"; };
|
||||
"irc.esper.net" = ircServer {
|
||||
name = "esper";
|
||||
sasl = false;
|
||||
# notable channels:
|
||||
# - #merveilles
|
||||
};
|
||||
"irc.libera.chat" = ircServer {
|
||||
name = "libera";
|
||||
sasl = false;
|
||||
# notable channels:
|
||||
# - #hare
|
||||
};
|
||||
"irc.myanonamouse.net" = ircServer {
|
||||
name = "MyAnonamouse";
|
||||
additionalAddresses = [ "irc2.myanonamouse.net" ];
|
||||
sasl = false;
|
||||
};
|
||||
"irc.oftc.net" = ircServer {
|
||||
name = "oftc";
|
||||
# notable channels:
|
||||
# - #sxmo
|
||||
# - #sxmo-offtopic
|
||||
};
|
||||
"irc.rizon.net" = ircServer { name = "Rizon"; };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@@ -13,7 +13,19 @@ let
|
||||
in
|
||||
{
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||
sane.ports.ports."80" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
visibleTo.ovpn = true; # so that letsencrypt can procure a cert for the mx record
|
||||
description = "colin-http-uninsane.org";
|
||||
};
|
||||
sane.ports.ports."443" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-https-uninsane.org";
|
||||
};
|
||||
|
||||
services.nginx.enable = true;
|
||||
services.nginx.appendConfig = ''
|
||||
|
@@ -17,5 +17,5 @@
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."nixcache" = "native";
|
||||
|
||||
sane.services.nixserve.enable = true;
|
||||
sane.services.nixserve.sopsFile = ../../../../secrets/servo.yaml;
|
||||
sane.services.nixserve.secretKeyFile = config.sops.secrets.nix_serve_privkey.path;
|
||||
}
|
||||
|
23
hosts/by-name/servo/services/pict-rs.nix
Normal file
23
hosts/by-name/servo/services/pict-rs.nix
Normal file
@@ -0,0 +1,23 @@
|
||||
# pict-rs is an image database/store used by Lemmy.
|
||||
# i don't explicitly activate it here -- just adjust its defaults to be a bit friendlier
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.services.pict-rs;
|
||||
in
|
||||
{
|
||||
sane.persist.sys.plaintext = lib.mkIf cfg.enable [
|
||||
{ user = "pict-rs"; group = "pict-rs"; directory = cfg.dataDir; }
|
||||
];
|
||||
|
||||
systemd.services.pict-rs.serviceConfig = {
|
||||
# fix to use a normal user so we can configure perms correctly
|
||||
DynamicUser = lib.mkForce false;
|
||||
User = "pict-rs";
|
||||
Group = "pict-rs";
|
||||
};
|
||||
users.groups.pict-rs = {};
|
||||
users.users.pict-rs = {
|
||||
group = "pict-rs";
|
||||
isSystemUser = true;
|
||||
};
|
||||
}
|
@@ -12,12 +12,29 @@ lib.mkIf false
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "prosody"; group = "prosody"; directory = "/var/lib/prosody"; }
|
||||
];
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
5222 # XMPP client -> server
|
||||
5269 # XMPP server -> server
|
||||
5280 # bosh
|
||||
5281 # Prosody HTTPS port (necessary?)
|
||||
];
|
||||
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" ];
|
||||
|
@@ -27,7 +27,7 @@
|
||||
# units in kBps
|
||||
speed-limit-down = 3000;
|
||||
speed-limit-down-enabled = true;
|
||||
speed-limit-up = 300;
|
||||
speed-limit-up = 600;
|
||||
speed-limit-up-enabled = true;
|
||||
|
||||
# see: https://git.zknt.org/mirror/transmission/commit/cfce6e2e3a9b9d31a9dafedd0bdc8bf2cdb6e876?lang=bg-BG
|
||||
|
@@ -30,17 +30,20 @@
|
||||
7d ; Expire
|
||||
5m) ; Negative response TTL
|
||||
'';
|
||||
TXT."rev" = "2022122101";
|
||||
TXT."rev" = "2023052901";
|
||||
|
||||
CNAME."native" = "%CNAMENATIVE%";
|
||||
A."@" = "%ANATIVE%";
|
||||
A."wan" = "%AWAN%";
|
||||
A."servo.lan" = config.sane.hosts.by-name."servo".lan-ip;
|
||||
|
||||
# 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.
|
||||
# so, org. can specify ns2/ns3 as being to the VPN, with no mention of ns1. we provide ns1 here.
|
||||
A."ns1" = "%NATIVE%";
|
||||
A."ns1" = "%ANATIVE%";
|
||||
A."ns2" = "185.157.162.178";
|
||||
A."ns3" = "185.157.162.178";
|
||||
A."ovpns" = "185.157.162.178";
|
||||
A."native" = "%NATIVE%";
|
||||
A."@" = "%NATIVE%";
|
||||
NS."@" = [
|
||||
"ns1.uninsane.org."
|
||||
"ns2.uninsane.org."
|
||||
@@ -48,20 +51,68 @@
|
||||
];
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".file =
|
||||
"/var/lib/trust-dns/uninsane.org.zone";
|
||||
sane.services.trust-dns.zones."uninsane.org".file = "uninsane.org.zone";
|
||||
sane.services.trust-dns.zonedir = null;
|
||||
|
||||
systemd.services.trust-dns.preStart = let
|
||||
sed = "${pkgs.gnused}/bin/sed";
|
||||
zone-dir = "/var/lib/trust-dns";
|
||||
zone-out = "${zone-dir}/uninsane.org.zone";
|
||||
zone-template = pkgs.writeText "uninsane.org.zone.in" config.sane.services.trust-dns.generatedZones."uninsane.org";
|
||||
in ''
|
||||
# make WAN records available to trust-dns
|
||||
mkdir -p ${zone-dir}
|
||||
ip=$(cat '${config.sane.services.dyn-dns.ipPath}')
|
||||
${sed} s/%NATIVE%/$ip/ ${zone-template} > ${zone-out}
|
||||
'';
|
||||
sane.services.trust-dns.package =
|
||||
let
|
||||
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 = config.sane.services.trust-dns.zones."uninsane.org".file;
|
||||
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
|
||||
'';
|
||||
|
||||
sane.services.dyn-dns.restartOnChange = [ "trust-dns.service" ];
|
||||
|
||||
networking.nat.enable = true;
|
||||
networking.nat.extraCommands = ''
|
||||
# redirect incoming DNS requests from LAN addresses
|
||||
# to the LAN-specialized DNS service
|
||||
# N.B.: use the `nixos-*` chains instead of e.g. PREROUTING
|
||||
# because they get cleanly reset across activations or `systemctl restart firewall`
|
||||
# instead of accumulating cruft
|
||||
iptables -t nat -A nixos-nat-pre -p udp --dport 53 \
|
||||
-m iprange --src-range 10.78.76.0-10.78.79.255 \
|
||||
-j DNAT --to-destination :1053
|
||||
iptables -t nat -A nixos-nat-pre -p tcp --dport 53 \
|
||||
-m iprange --src-range 10.78.76.0-10.78.79.255 \
|
||||
-j DNAT --to-destination :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.
|
||||
# TODO: try nixos-nat-post instead?
|
||||
protocol = [ "udp" "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
description = "colin-redirected-dns-for-lan-namespace";
|
||||
};
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,14 @@
|
||||
{ lib, pkgs, ... }:
|
||||
{
|
||||
imports = [
|
||||
./cross
|
||||
./feeds.nix
|
||||
./fs.nix
|
||||
./hardware.nix
|
||||
./home
|
||||
./i2p.nix
|
||||
./ids.nix
|
||||
./machine-id.nix
|
||||
./net.nix
|
||||
./nix-path
|
||||
./persist.nix
|
||||
./programs
|
||||
./secrets.nix
|
||||
@@ -28,6 +27,7 @@
|
||||
sane.fs."/var/lib/private".dir.acl.mode = "0700";
|
||||
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
nixpkgs.config.allowBroken = true; # NIXPKGS_ALLOW_BROKEN
|
||||
|
||||
# time.timeZone = "America/Los_Angeles";
|
||||
time.timeZone = "Etc/UTC"; # DST is too confusing for me => use a stable timezone
|
||||
@@ -37,11 +37,6 @@
|
||||
nix.extraOptions = ''
|
||||
experimental-features = nix-command flakes
|
||||
'';
|
||||
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages
|
||||
nix.nixPath = [
|
||||
"nixpkgs=${pkgs.path}"
|
||||
"nixpkgs-overlays=${../..}/overlays"
|
||||
];
|
||||
# hardlinks identical files in the nix store to save 25-35% disk space.
|
||||
# unclear _when_ this occurs. it's not a service.
|
||||
# does the daemon continually scan the nix store?
|
||||
|
@@ -65,7 +65,7 @@ let
|
||||
## Maggie Killjoy -- referenced by Cory Doctorow
|
||||
(fromDb "omny.fm/shows/cool-people-who-did-cool-stuff" // pol)
|
||||
(fromDb "congressionaldish.libsyn.com" // pol)
|
||||
(mkPod "https://podcasts.la.utexas.edu/this-is-democracy/feed/podcast/" // pol // weekly)
|
||||
# (mkPod "https://podcasts.la.utexas.edu/this-is-democracy/feed/podcast/" // pol // weekly)
|
||||
## Civboot -- https://anchor.fm/civboot
|
||||
(fromDb "anchor.fm/s/34c7232c/podcast/rss" // tech)
|
||||
## Emerge: making sense of what's next -- <https://www.whatisemerging.com/emergepodcast>
|
||||
@@ -76,7 +76,7 @@ let
|
||||
## Multidisciplinary Association for Psychedelic Studies
|
||||
(fromDb "mapspodcast.libsyn.com" // uncat)
|
||||
(fromDb "allinchamathjason.libsyn.com" // pol)
|
||||
(fromDb "acquired.libsyn.com" // tech)
|
||||
(fromDb "feeds.transistor.fm/acquired" // tech)
|
||||
## ACQ2 - more "Acquired" episodes
|
||||
(fromDb "acquiredlpbonussecretsecret.libsyn.com" // tech)
|
||||
# The Intercept - Deconstructed; also available: <rss.acast.com/deconstructed>
|
||||
@@ -125,7 +125,9 @@ let
|
||||
(fromDb "profectusmag.com" // uncat)
|
||||
(fromDb "semiaccurate.com" // tech)
|
||||
(mkText "https://linuxphoneapps.org/blog/atom.xml" // tech // infrequent)
|
||||
(fromDb "tuxphones.com" // tech)
|
||||
(fromDb "spectrum.ieee.org" // tech)
|
||||
(fromDb "theregister.com" // tech)
|
||||
(fromDb "thisweek.gnome.org" // tech)
|
||||
# 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)
|
||||
@@ -142,7 +144,7 @@ let
|
||||
(fromDb "ascii.textfiles.com" // tech) # Jason Scott
|
||||
(fromDb "xn--gckvb8fzb.com" // tech)
|
||||
(fromDb "mg.lol" // tech)
|
||||
(fromDb "drewdevault.com" // tech)
|
||||
# (fromDb "drewdevault.com" // tech)
|
||||
## Ken Shirriff
|
||||
(fromDb "righto.com" // tech)
|
||||
## shared blog by a few NixOS devs, notably onny
|
||||
@@ -189,6 +191,7 @@ let
|
||||
(fromDb "stpeter.im/atom.xml" // pol)
|
||||
## Peter Saint-Andre -- side project of stpeter.im
|
||||
(fromDb "philosopher.coach" // rat)
|
||||
(fromDb "morningbrew.com/feed" // pol)
|
||||
|
||||
# RATIONALITY/PHILOSOPHY/ETC
|
||||
(mkSubstack "samkriss" // humor // infrequent)
|
||||
@@ -207,6 +210,8 @@ let
|
||||
(fromDb "sideways-view.com" // rat)
|
||||
## Sean Carroll
|
||||
(fromDb "preposterousuniverse.com" // rat)
|
||||
(mkSubstack "eliqian" // rat // weekly)
|
||||
(mkText "https://acoup.blog/feed" // rat // weekly)
|
||||
|
||||
## mostly dating topics. not advice, or humor, but looking through a social lens
|
||||
(fromDb "putanumonit.com" // rat)
|
||||
|
@@ -28,6 +28,11 @@
|
||||
# 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 = "/";
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{ config, sane-lib, ...}:
|
||||
|
||||
let
|
||||
www = config.sane.web-browser.browser.desktop;
|
||||
www = config.sane.programs.web-browser.config.browser.desktop;
|
||||
pdf = "org.gnome.Evince.desktop";
|
||||
md = "obsidian.desktop";
|
||||
thumb = "org.gnome.gThumb.desktop";
|
||||
@@ -28,6 +28,7 @@ in
|
||||
# VIDEO
|
||||
"video/mp4" = video;
|
||||
"video/quicktime" = video;
|
||||
"video/webm" = video;
|
||||
"video/x-matroska" = video;
|
||||
# HTML
|
||||
"text/html" = www;
|
||||
|
@@ -1,4 +0,0 @@
|
||||
{ ... }:
|
||||
{
|
||||
services.i2p.enable = true;
|
||||
}
|
@@ -38,6 +38,8 @@
|
||||
sane.ids.komga.gid = 2407;
|
||||
sane.ids.lemmy.uid = 2408;
|
||||
sane.ids.lemmy.gid = 2408;
|
||||
sane.ids.pict-rs.uid = 2409;
|
||||
sane.ids.pict-rs.gid = 2409;
|
||||
|
||||
sane.ids.colin.uid = 1000;
|
||||
sane.ids.guest.uid = 1100;
|
||||
@@ -51,6 +53,7 @@
|
||||
sane.ids.nscd.gid = 2004;
|
||||
sane.ids.systemd-oom.uid = 2005;
|
||||
sane.ids.systemd-oom.gid = 2005;
|
||||
sane.ids.wireshark.gid = 2006;
|
||||
|
||||
# found on graphical hosts
|
||||
sane.ids.nm-iodine.uid = 2101; # desko/moby/lappy
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
# the default backend is "wpa_supplicant".
|
||||
@@ -20,4 +20,8 @@
|
||||
General.RoamThreshold = "-52"; # default -70
|
||||
General.RoamThreshold5G = "-52"; # default -76
|
||||
};
|
||||
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
1900 # to received UPnP advertisements. required by sane-ip-check-upnp
|
||||
];
|
||||
}
|
||||
|
10
hosts/common/nix-path/default.nix
Normal file
10
hosts/common/nix-path/default.nix
Normal file
@@ -0,0 +1,10 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages
|
||||
nix.nixPath = [
|
||||
"nixpkgs=${pkgs.path}"
|
||||
# note the import starts at repo root: this allows `./overlay/default.nix` to access the stuff at the root
|
||||
"nixpkgs-overlays=${../../..}/hosts/common/nix-path/overlay"
|
||||
];
|
||||
}
|
4
hosts/common/nix-path/overlay/default.nix
Normal file
4
hosts/common/nix-path/overlay/default.nix
Normal file
@@ -0,0 +1,4 @@
|
||||
# XXX: NIX_PATH=...:nixpkgs-overlays=... will import every overlay in the directory
|
||||
# so we prefer to give it a directory with just this *one* overlay, otherwise it imports conflicting overlays
|
||||
# and gets stuck in a loop until it OOMs
|
||||
import ../../../../overlays/all.nix
|
@@ -1,6 +1,6 @@
|
||||
# Terminal UI mail client
|
||||
{ config, sane-lib, ... }:
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
sane.programs.aerc.secrets.".config/aerc/accounts.conf" = ../../../secrets/universal/aerc_accounts.conf.bin;
|
||||
sane.programs.aerc.secrets.".config/aerc/accounts.conf" = ../../../secrets/common/aerc_accounts.conf.bin;
|
||||
}
|
||||
|
@@ -42,6 +42,7 @@ let
|
||||
jq
|
||||
killall
|
||||
lsof
|
||||
miniupnpc
|
||||
nano
|
||||
netcat
|
||||
nethogs
|
||||
@@ -61,6 +62,7 @@ let
|
||||
tree
|
||||
usbutils
|
||||
wget
|
||||
wirelesstools # iwlist
|
||||
;
|
||||
};
|
||||
sysadminExtraPkgs = {
|
||||
@@ -93,6 +95,7 @@ let
|
||||
# - transcoders (ffmpeg, imagemagick) only wanted on desko/lappy ("powerutils"?)
|
||||
consolePkgs = {
|
||||
inherit (pkgs)
|
||||
alsaUtils # for aplay, speaker-test
|
||||
cdrtools
|
||||
dmidecode
|
||||
efivar
|
||||
@@ -102,7 +105,7 @@ let
|
||||
git # needed as a user package, for config.
|
||||
gnupg
|
||||
gocryptfs
|
||||
gopass
|
||||
gopass # TODO: shouldn't be needed here
|
||||
gopass-jsonapi
|
||||
imagemagick
|
||||
kitty # TODO: move to GUI, but `ssh servo` from kitty sets `TERM=xterm-kitty` in the remove and breaks things
|
||||
@@ -110,7 +113,7 @@ let
|
||||
lm_sensors # for sensors-detect
|
||||
lshw
|
||||
ffmpeg
|
||||
memtester
|
||||
# memtester
|
||||
neovim
|
||||
# nettools
|
||||
# networkmanager
|
||||
@@ -118,7 +121,7 @@ let
|
||||
# nixos-generators
|
||||
nmon
|
||||
# node2nix
|
||||
oathToolkit # for oathtool
|
||||
# oathToolkit # for oathtool
|
||||
# ponymix
|
||||
pulsemixer
|
||||
python3
|
||||
@@ -131,7 +134,7 @@ let
|
||||
sops
|
||||
sox
|
||||
speedtest-cli
|
||||
ssh-to-age
|
||||
# ssh-to-age
|
||||
sudo
|
||||
# tageditor # music tagging
|
||||
unar
|
||||
@@ -146,7 +149,7 @@ let
|
||||
|
||||
guiPkgs = {
|
||||
inherit (flattenedPkgs)
|
||||
celluloid # mpv frontend
|
||||
# celluloid # mpv frontend
|
||||
clinfo
|
||||
emote
|
||||
evince # works on phosh
|
||||
@@ -163,19 +166,19 @@ let
|
||||
|
||||
# "gnome.cheese"
|
||||
"gnome.dconf-editor"
|
||||
gnome-feeds # RSS reader (with claimed mobile support)
|
||||
# gnome-feeds # RSS reader (with claimed mobile support)
|
||||
"gnome.file-roller"
|
||||
# "gnome.gnome-maps" # works on phosh
|
||||
"gnome.nautilus"
|
||||
# gnome-podcasts
|
||||
"gnome.gnome-system-monitor"
|
||||
# "gnome.gnome-terminal" # works on phosh
|
||||
"gnome.gnome-weather"
|
||||
gpodder-configured
|
||||
# "gnome.gnome-weather"
|
||||
gpodder
|
||||
gthumb
|
||||
jellyfin-media-player
|
||||
# lollypop
|
||||
mpv
|
||||
# mpv
|
||||
networkmanagerapplet
|
||||
# newsflash
|
||||
nheko
|
||||
@@ -212,11 +215,12 @@ let
|
||||
kdenlive
|
||||
kid3 # audio tagging
|
||||
krita
|
||||
libreoffice-fresh # XXX colin: maybe don't want this on mobile
|
||||
libreoffice-fresh
|
||||
mumble
|
||||
obsidian
|
||||
slic3r
|
||||
steam
|
||||
wireshark # could maybe ship the cli as sysadmin pkg
|
||||
;
|
||||
};
|
||||
x86GuiPkgs = {
|
||||
@@ -227,21 +231,21 @@ let
|
||||
# gnome.zenity # for kaiteki (it will use qarma, kdialog, or zenity)
|
||||
# gpt2tc # XXX: unreliable mirror
|
||||
|
||||
logseq
|
||||
# logseq # Personal Knowledge Management
|
||||
losslesscut-bin
|
||||
makemkv
|
||||
monero-gui
|
||||
signal-desktop
|
||||
spotify
|
||||
tor-browser-bundle-bin
|
||||
zeal-qt5 # programming docs viewer. TODO: switch to zeal-qt6
|
||||
zecwallet-lite
|
||||
;
|
||||
};
|
||||
|
||||
# packages not part of any package set
|
||||
# packages not part of any package set; not enabled by default
|
||||
otherPkgs = {
|
||||
inherit (pkgs)
|
||||
lemmy-server
|
||||
mx-sanebot
|
||||
stepmania
|
||||
;
|
||||
@@ -260,6 +264,7 @@ in
|
||||
./git.nix
|
||||
./gnome-feeds.nix
|
||||
./gpodder.nix
|
||||
./jellyfin-media-player.nix
|
||||
./kitty
|
||||
./libreoffice.nix
|
||||
./mpv.nix
|
||||
@@ -271,6 +276,7 @@ in
|
||||
./sublime-music.nix
|
||||
./vlc.nix
|
||||
./web-browser.nix
|
||||
./wireshark.nix
|
||||
./zeal.nix
|
||||
./zsh
|
||||
];
|
||||
@@ -299,6 +305,7 @@ in
|
||||
guiApps = {
|
||||
package = null;
|
||||
suggestedPrograms = (attrNames guiPkgs)
|
||||
++ [ "web-browser" ]
|
||||
++ [ "tuiApps" ]
|
||||
++ optional (pkgs.system == "x86_64-linux") "x86GuiApps";
|
||||
};
|
||||
@@ -345,10 +352,6 @@ in
|
||||
|
||||
ghostscript = {}; # used by imagemagick
|
||||
|
||||
# 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.
|
||||
gpodder-configured.persist.plaintext = [ "gPodder" ];
|
||||
|
||||
imagemagick = {
|
||||
package = pkgs.imagemagick.override {
|
||||
ghostscriptSupport = true;
|
||||
@@ -356,11 +359,6 @@ in
|
||||
suggestedPrograms = [ "ghostscript" ];
|
||||
};
|
||||
|
||||
# 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
|
||||
# be fragile and take an hour+ to figure out.
|
||||
jellyfin-media-player.persist.plaintext = [ ".local/share/Jellyfin Media Player" ];
|
||||
|
||||
# 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?
|
||||
monero-gui.persist.plaintext = [ ".bitmonero" ];
|
||||
|
@@ -1,18 +1,26 @@
|
||||
{ lib, pkgs, sane-lib, ... }:
|
||||
{ lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
mkCfg = lib.generators.toINI { };
|
||||
in
|
||||
{
|
||||
sane.programs.git.fs.".config/git/config" = sane-lib.fs.wantedText (mkCfg {
|
||||
sane.programs.git.fs.".config/git/config".symlink.text = mkCfg {
|
||||
# top-level options documented:
|
||||
# - <https://git-scm.com/docs/git-config#_variables>
|
||||
|
||||
user.name = "Colin";
|
||||
user.email = "colin@uninsane.org";
|
||||
|
||||
alias.co = "checkout";
|
||||
|
||||
# difftastic docs:
|
||||
# - <https://difftastic.wilfred.me.uk/git.html>
|
||||
diff.tool = "difftastic";
|
||||
difftool.prompt = false;
|
||||
"difftool \"difftastic\"".cmd = ''${pkgs.difftastic}/bin/difft "$LOCAL" "$REMOTE"'';
|
||||
# now run `git difftool` to use difftastic git
|
||||
});
|
||||
|
||||
# render dates as YYYY-MM-DD HH:MM:SS +TZ
|
||||
log.date = "iso";
|
||||
};
|
||||
}
|
||||
|
@@ -6,37 +6,35 @@ let
|
||||
all-feeds = config.sane.feeds;
|
||||
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
|
||||
in {
|
||||
sane.programs.gnome-feeds.fs.".config/org.gabmus.gfeeds.json" = sane-lib.fs.wantedText (
|
||||
builtins.toJSON {
|
||||
# feed format is a map from URL to a dict,
|
||||
# with dict["tags"] a list of string tags.
|
||||
feeds = sane-lib.mapToAttrs (feed: {
|
||||
name = feed.url;
|
||||
value.tags = [ feed.cat feed.freq ];
|
||||
}) wanted-feeds;
|
||||
dark_reader = false;
|
||||
new_first = true;
|
||||
# windowsize = {
|
||||
# width = 350;
|
||||
# height = 650;
|
||||
# };
|
||||
max_article_age_days = 90;
|
||||
enable_js = false;
|
||||
max_refresh_threads = 3;
|
||||
# saved_items = {};
|
||||
# read_items = [];
|
||||
show_read_items = true;
|
||||
full_article_title = true;
|
||||
# views: "webview", "reader", "rsscont"
|
||||
default_view = "rsscont";
|
||||
open_links_externally = true;
|
||||
full_feed_name = false;
|
||||
refresh_on_startup = true;
|
||||
tags = lib.unique (
|
||||
(builtins.catAttrs "cat" wanted-feeds) ++ (builtins.catAttrs "freq" wanted-feeds)
|
||||
);
|
||||
open_youtube_externally = false;
|
||||
media_player = "vlc"; # default: mpv
|
||||
}
|
||||
);
|
||||
sane.programs.gnome-feeds.fs.".config/org.gabmus.gfeeds.json".symlink.text = builtins.toJSON {
|
||||
# feed format is a map from URL to a dict,
|
||||
# with dict["tags"] a list of string tags.
|
||||
feeds = sane-lib.mapToAttrs (feed: {
|
||||
name = feed.url;
|
||||
value.tags = [ feed.cat feed.freq ];
|
||||
}) wanted-feeds;
|
||||
dark_reader = false;
|
||||
new_first = true;
|
||||
# windowsize = {
|
||||
# width = 350;
|
||||
# height = 650;
|
||||
# };
|
||||
max_article_age_days = 90;
|
||||
enable_js = false;
|
||||
max_refresh_threads = 3;
|
||||
# saved_items = {};
|
||||
# read_items = [];
|
||||
show_read_items = true;
|
||||
full_article_title = true;
|
||||
# views: "webview", "reader", "rsscont"
|
||||
default_view = "rsscont";
|
||||
open_links_externally = true;
|
||||
full_feed_name = false;
|
||||
refresh_on_startup = true;
|
||||
tags = lib.unique (
|
||||
(builtins.catAttrs "cat" wanted-feeds) ++ (builtins.catAttrs "freq" wanted-feeds)
|
||||
);
|
||||
open_youtube_externally = false;
|
||||
media_player = "vlc"; # default: mpv
|
||||
};
|
||||
}
|
||||
|
@@ -1,12 +1,17 @@
|
||||
# gnome feeds RSS viewer
|
||||
{ config, sane-lib, ... }:
|
||||
{ config, pkgs, sane-lib, ... }:
|
||||
|
||||
let
|
||||
feeds = sane-lib.feeds;
|
||||
all-feeds = config.sane.feeds;
|
||||
wanted-feeds = feeds.filterByFormat ["podcast"] all-feeds;
|
||||
in {
|
||||
sane.programs.gpodder.fs.".config/gpodderFeeds.opml" = sane-lib.fs.wantedText (
|
||||
feeds.feedsToOpml wanted-feeds
|
||||
);
|
||||
sane.programs.gpodder = {
|
||||
package = pkgs.gpodder-configured;
|
||||
fs.".config/gpodderFeeds.opml".symlink.text = feeds.feedsToOpml wanted-feeds;
|
||||
|
||||
# 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.
|
||||
persist.plaintext = [ "gPodder" ];
|
||||
};
|
||||
}
|
||||
|
13
hosts/common/programs/jellyfin-media-player.nix
Normal file
13
hosts/common/programs/jellyfin-media-player.nix
Normal file
@@ -0,0 +1,13 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
sane.programs.jellyfin-media-player = {
|
||||
package = pkgs.jellyfin-media-player;
|
||||
# package = pkgs.jellyfin-media-player-qt6;
|
||||
|
||||
# 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
|
||||
# be fragile and take an hour+ to figure out.
|
||||
persist.plaintext = [ ".local/share/Jellyfin Media Player" ];
|
||||
};
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{ pkgs, sane-lib, ... }:
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
sane.programs.kitty.fs.".config/kitty/kitty.conf" = sane-lib.fs.wantedText ''
|
||||
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
|
||||
|
@@ -1,8 +1,8 @@
|
||||
{ sane-lib, ... }:
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
# libreoffice: disable first-run stuff
|
||||
sane.programs.libreoffice-fresh.fs.".config/libreoffice/4/user/registrymodifications.xcu" = sane-lib.fs.wantedText ''
|
||||
sane.programs.libreoffice-fresh.fs.".config/libreoffice/4/user/registrymodifications.xcu".symlink.text = ''
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<oor:items xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<item oor:path="/org.openoffice.Office.Common/Misc"><prop oor:name="FirstRun" oor:op="fuse"><value>false</value></prop></item>
|
||||
|
@@ -1,10 +1,10 @@
|
||||
{ sane-lib, ... }:
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
sane.programs.mpv = {
|
||||
persist.plaintext = [ ".config/mpv/watch_later" ];
|
||||
# format is <key>=%<length>%<value>
|
||||
fs.".config/mpv/mpv.conf" = sane-lib.fs.wantedText ''
|
||||
fs.".config/mpv/mpv.conf".symlink.text = ''
|
||||
save-position-on-quit=%3%yes
|
||||
keep-open=%3%yes
|
||||
'';
|
||||
|
@@ -8,8 +8,8 @@ let
|
||||
in {
|
||||
sane.programs.newsflash = {
|
||||
persist.plaintext = [ ".local/share/news-flash" ];
|
||||
fs.".config/newsflashFeeds.opml" = sane-lib.fs.wantedText (
|
||||
fs.".config/newsflashFeeds.opml".symlink.text =
|
||||
feeds.feedsToOpml wanted-feeds
|
||||
);
|
||||
;
|
||||
};
|
||||
}
|
||||
|
@@ -7,6 +7,6 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
sane.programs.offlineimap.secrets.".config/offlineimap/config" = ../../../secrets/universal/offlineimaprc.bin;
|
||||
sane.programs.offlineimap.secrets.".config/offlineimap/config" = ../../../secrets/common/offlineimaprc.bin;
|
||||
}
|
||||
|
||||
|
@@ -1,9 +1,9 @@
|
||||
{ sane-lib, ... }:
|
||||
{ ... }:
|
||||
{
|
||||
# .ignore file is read by ripgrep (rg), silver searcher (ag), maybe others.
|
||||
# ignore translation files by default when searching, as they tend to have
|
||||
# a LOT of duplicate text.
|
||||
sane.programs.ripgrep.fs.".ignore" = sane-lib.fs.wantedText ''
|
||||
sane.programs.ripgrep.fs.".ignore".symlink.text = ''
|
||||
po/
|
||||
'';
|
||||
}
|
||||
|
@@ -1,12 +1,12 @@
|
||||
# borrows from:
|
||||
# - default config: <https://github.com/cspeterson/splatmoji/blob/master/splatmoji.config>
|
||||
# - wayland: <https://github.com/cspeterson/splatmoji/issues/32#issuecomment-830862566>
|
||||
{ pkgs, sane-lib, ... }:
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
sane.programs.splatmoji = {
|
||||
persist.plaintext = [ ".local/state/splatmoji" ];
|
||||
fs.".config/splatmoji/splatmoji.config" = sane-lib.fs.wantedText ''
|
||||
fs.".config/splatmoji/splatmoji.config".symlink.text = ''
|
||||
# XXX doesn't seem to understand ~ as shorthand for `$HOME`
|
||||
history_file=/home/colin/.local/state/splatmoji/history
|
||||
history_length=5
|
||||
|
@@ -9,6 +9,6 @@
|
||||
# possible to pass config as a CLI arg (sublime-music -c config.json)
|
||||
persist.plaintext = [ ".local/share/sublime-music" ];
|
||||
|
||||
secrets.".config/sublime-music/config.json" = ../../../secrets/universal/sublime_music_config.json.bin;
|
||||
secrets.".config/sublime-music/config.json" = ../../../secrets/common/sublime_music_config.json.bin;
|
||||
};
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ in
|
||||
sane.programs.vlc = {
|
||||
# vlc remembers play position in ~/.config/vlc/vlc-qt-interface.conf
|
||||
persist.plaintext = [ ".config/vlc" ];
|
||||
fs.".config/vlc/vlcrc" = sane-lib.fs.wantedText ''
|
||||
fs.".config/vlc/vlcrc".symlink.text = ''
|
||||
[podcast]
|
||||
podcast-urls=${podcast-urls}
|
||||
[core]
|
||||
|
@@ -6,10 +6,10 @@
|
||||
# many of the settings below won't have effect without those patches.
|
||||
# see: https://gitlab.com/librewolf-community/settings/-/blob/master/distribution/policies.json
|
||||
|
||||
{ config, lib, pkgs, sane-lib, ...}:
|
||||
{ config, lib, pkgs, ...}:
|
||||
with lib;
|
||||
let
|
||||
cfg = config.sane.web-browser;
|
||||
cfg = config.sane.programs.web-browser.config;
|
||||
# allow easy switching between firefox and librewolf with `defaultSettings`, below
|
||||
librewolfSettings = {
|
||||
browser = pkgs.librewolf-unwrapped;
|
||||
@@ -50,16 +50,32 @@ let
|
||||
inherit (pkgs.librewolf-unwrapped) extraPrefsFiles;
|
||||
inherit (cfg.browser) libName;
|
||||
|
||||
extraNativeMessagingHosts = [ pkgs.browserpass ];
|
||||
extraNativeMessagingHosts = optional cfg.addons.browserpass-extension.enable pkgs.browserpass;
|
||||
# extraNativeMessagingHosts = [ pkgs.gopass-native-messaging-host ];
|
||||
|
||||
nixExtensions = concatMap (ext: optional ext.enable ext.package) (attrValues cfg.addons);
|
||||
|
||||
extraPolicies = {
|
||||
FirefoxHome = {
|
||||
Search = true;
|
||||
Pocket = false;
|
||||
Snippets = false;
|
||||
TopSites = false;
|
||||
Highlights = false;
|
||||
};
|
||||
NoDefaultBookmarks = true;
|
||||
OfferToSaveLogins = false;
|
||||
OfferToSaveLoginsDefault = false;
|
||||
PasswordManagerEnabled = false;
|
||||
SearchEngines = {
|
||||
Default = "DuckDuckGo";
|
||||
};
|
||||
UserMessaging = {
|
||||
ExtensionRecommendations = false;
|
||||
SkipOnboarding = true;
|
||||
};
|
||||
|
||||
# these were taken from Librewolf
|
||||
AppUpdateURL = "https://localhost";
|
||||
DisableAppUpdate = true;
|
||||
OverrideFirstRunPage = "";
|
||||
@@ -88,6 +104,7 @@ let
|
||||
# };
|
||||
# NewTabPage = true;
|
||||
};
|
||||
# extraPrefs = ...
|
||||
};
|
||||
|
||||
addonOpts = types.submodule {
|
||||
@@ -100,55 +117,78 @@ let
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sane.web-browser.browser = mkOption {
|
||||
default = defaultSettings;
|
||||
type = types.attrs;
|
||||
};
|
||||
sane.web-browser.persistData = mkOption {
|
||||
description = "optional store name to which persist browsing data (like history)";
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
sane.web-browser.persistCache = mkOption {
|
||||
description = "optional store name to which persist browser cache";
|
||||
type = types.nullOr types.str;
|
||||
default = "cryptClearOnBoot";
|
||||
};
|
||||
sane.web-browser.addons = mkOption {
|
||||
type = types.attrsOf addonOpts;
|
||||
default = {
|
||||
# get names from:
|
||||
# - ~/ref/nix-community/nur-combined/repos/rycee/pkgs/firefox-addons/generated-firefox-addons.nix
|
||||
# `wget ...xpi`; `unar ...xpi`; `cat */manifest.json | jq '.browser_specific_settings.gecko.id'`
|
||||
# browserpass-ce.package = addon "browserpass-ce" "browserpass@maximbaz.com" "sha256-sXgUBbRvMnRpeIW1MTkmTcoqtW/8RDXAkxAq1evFkpc=";
|
||||
browserpass-extension.package = localAddon pkgs.browserpass-extension;
|
||||
# TODO: build bypass-paywalls from source? it's mysteriously disappeared from the Mozilla store.
|
||||
# bypass-paywalls-clean.package = addon "bypass-paywalls-clean" "{d133e097-46d9-4ecc-9903-fa6a722a6e0e}" "sha256-oUwdqdAwV3DezaTtOMx7A/s4lzIws+t2f08mwk+324k=";
|
||||
ether-metamask.package = addon "ether-metamask" "webextension@metamask.io" "sha256-G+MwJDOcsaxYSUXjahHJmkWnjLeQ0Wven8DU/lGeMzA=";
|
||||
i2p-in-private-browsing.package = addon "i2p-in-private-browsing" "i2ppb@eyedeekay.github.io" "sha256-dJcJ3jxeAeAkRvhODeIVrCflvX+S4E0wT/PyYzQBQWs=";
|
||||
sidebery.package = addon "sidebery" "{3c078156-979c-498b-8990-85f7987dd929}" "sha256-YONfK/rIjlsrTgRHIt3km07Q7KnpIW89Z9r92ZSCc6w=";
|
||||
sponsorblock.package = addon "sponsorblock" "sponsorBlocker@ajay.app" "sha256-hRsvLaAsVm3dALsTrJqHTNgRFAQcU7XSaGhr5G6+mFs=";
|
||||
ublacklist.package = addon "ublacklist" "@ublacklist" "sha256-RqY5iHzbL2qizth7aguyOKWPyINXmrwOlf/OsfqAS48=";
|
||||
ublock-origin.package = addon "ublock-origin" "uBlock0@raymondhill.net" "sha256-eHlQrU/b9X/6sTbHBpGAd+0VsLT7IrVCnd0AQ948lyA=";
|
||||
|
||||
browserpass-extension.enable = lib.mkDefault true;
|
||||
# bypass-paywalls-clean.enable = lib.mkDefault true;
|
||||
ether-metamask.enable = lib.mkDefault true;
|
||||
i2p-in-private-browsing.enable = lib.mkDefault config.services.i2p.enable;
|
||||
sidebery.enable = lib.mkDefault true;
|
||||
sponsorblock.enable = lib.mkDefault true;
|
||||
ublacklist.enable = lib.mkDefault true;
|
||||
ublock-origin.enable = lib.mkDefault true;
|
||||
configOpts = {
|
||||
options = {
|
||||
browser = mkOption {
|
||||
default = defaultSettings;
|
||||
type = types.anything;
|
||||
};
|
||||
persistData = mkOption {
|
||||
description = "optional store name to which persist browsing data (like history)";
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
persistCache = mkOption {
|
||||
description = "optional store name to which persist browser cache";
|
||||
type = types.nullOr types.str;
|
||||
default = "cryptClearOnBoot";
|
||||
};
|
||||
addons = mkOption {
|
||||
type = types.attrsOf addonOpts;
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
config = mkMerge [
|
||||
({
|
||||
sane.programs.guiApps.suggestedPrograms = [ "web-browser" ];
|
||||
sane.programs.web-browser.configOption = mkOption {
|
||||
type = types.submodule configOpts;
|
||||
default = {};
|
||||
};
|
||||
sane.programs.web-browser.config.addons = {
|
||||
# get names from:
|
||||
# - ~/ref/nix-community/nur-combined/repos/rycee/pkgs/firefox-addons/generated-firefox-addons.nix
|
||||
# `wget ...xpi`; `unar ...xpi`; `cat */manifest.json | jq '.browser_specific_settings.gecko.id'`
|
||||
browserpass-extension = {
|
||||
# package = addon "browserpass-ce" "browserpass@maximbaz.com" "sha256-sXgUBbRvMnRpeIW1MTkmTcoqtW/8RDXAkxAq1evFkpc=";
|
||||
package = localAddon pkgs.browserpass-extension;
|
||||
enable = lib.mkDefault true;
|
||||
};
|
||||
|
||||
# TODO: build bypass-paywalls from source? it's mysteriously disappeared from the Mozilla store.
|
||||
# bypass-paywalls-clean.package = addon "bypass-paywalls-clean" "{d133e097-46d9-4ecc-9903-fa6a722a6e0e}" "sha256-oUwdqdAwV3DezaTtOMx7A/s4lzIws+t2f08mwk+324k=";
|
||||
# bypass-paywalls-clean.enable = lib.mkDefault true;
|
||||
|
||||
ether-metamask = {
|
||||
package = addon "ether-metamask" "webextension@metamask.io" "sha256-G+MwJDOcsaxYSUXjahHJmkWnjLeQ0Wven8DU/lGeMzA=";
|
||||
enable = lib.mkDefault true;
|
||||
};
|
||||
i2p-in-private-browsing = {
|
||||
package = addon "i2p-in-private-browsing" "i2ppb@eyedeekay.github.io" "sha256-dJcJ3jxeAeAkRvhODeIVrCflvX+S4E0wT/PyYzQBQWs=";
|
||||
enable = lib.mkDefault config.services.i2p.enable;
|
||||
};
|
||||
sidebery = {
|
||||
package = addon "sidebery" "{3c078156-979c-498b-8990-85f7987dd929}" "sha256-YONfK/rIjlsrTgRHIt3km07Q7KnpIW89Z9r92ZSCc6w=";
|
||||
enable = lib.mkDefault true;
|
||||
};
|
||||
sponsorblock = {
|
||||
package = addon "sponsorblock" "sponsorBlocker@ajay.app" "sha256-hRsvLaAsVm3dALsTrJqHTNgRFAQcU7XSaGhr5G6+mFs=";
|
||||
enable = lib.mkDefault true;
|
||||
};
|
||||
ublacklist = {
|
||||
package = addon "ublacklist" "@ublacklist" "sha256-RqY5iHzbL2qizth7aguyOKWPyINXmrwOlf/OsfqAS48=";
|
||||
enable = lib.mkDefault true;
|
||||
};
|
||||
ublock-origin = {
|
||||
package = addon "ublock-origin" "uBlock0@raymondhill.net" "sha256-eHlQrU/b9X/6sTbHBpGAd+0VsLT7IrVCnd0AQ948lyA=";
|
||||
enable = lib.mkDefault true;
|
||||
};
|
||||
};
|
||||
})
|
||||
({
|
||||
sane.programs.web-browser = {
|
||||
inherit package;
|
||||
|
||||
@@ -160,7 +200,7 @@ in
|
||||
# the specific attribute path is found via scraping ublock code here:
|
||||
# - <https://github.com/gorhill/uBlock/blob/master/src/js/storage.js>
|
||||
# - <https://github.com/gorhill/uBlock/blob/master/assets/assets.json>
|
||||
fs."${cfg.browser.dotDir}/managed-storage/uBlock0@raymondhill.net.json" = sane-lib.fs.wantedText ''
|
||||
fs."${cfg.browser.dotDir}/managed-storage/uBlock0@raymondhill.net.json".symlink.text = ''
|
||||
{
|
||||
"name": "uBlock0@raymondhill.net",
|
||||
"description": "ignored",
|
||||
@@ -170,16 +210,16 @@ in
|
||||
}
|
||||
}
|
||||
'';
|
||||
fs."${cfg.browser.dotDir}/${cfg.browser.libName}.overrides.cfg" = sane-lib.fs.wantedText ''
|
||||
fs."${cfg.browser.dotDir}/${cfg.browser.libName}.overrides.cfg".symlink.text = ''
|
||||
// if we can't query the revocation status of a SSL cert because the issuer is offline,
|
||||
// treat it as unrevoked.
|
||||
// see: <https://librewolf.net/docs/faq/#im-getting-sec_error_ocsp_server_error-what-can-i-do>
|
||||
defaultPref("security.OCSP.require", false);
|
||||
'';
|
||||
fs."${cfg.browser.dotDir}/default" = sane-lib.fs.wantedDir;
|
||||
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).
|
||||
# XXX: the directory *must* exist, even if empty; Firefox will not create the directory itself.
|
||||
fs."${cfg.browser.dotDir}/profiles.ini" = sane-lib.fs.wantedText ''
|
||||
fs."${cfg.browser.dotDir}/profiles.ini".symlink.text = ''
|
||||
[Profile0]
|
||||
Name=default
|
||||
IsRelative=1
|
||||
|
5
hosts/common/programs/wireshark.nix
Normal file
5
hosts/common/programs/wireshark.nix
Normal file
@@ -0,0 +1,5 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
sane.programs.wireshark = {};
|
||||
programs.wireshark.enable = config.sane.programs.wireshark.enabled;
|
||||
}
|
@@ -1,16 +1,55 @@
|
||||
{ config, lib, sane-lib, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
inherit (lib) mkIf;
|
||||
inherit (builtins) map;
|
||||
inherit (lib) mkIf mkOption optionalString types;
|
||||
cfg = config.sane.programs.docsets.config;
|
||||
configOpts = types.submodule {
|
||||
options = {
|
||||
rustPkgs = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
in {
|
||||
sane.programs.zeal-qt5 = {
|
||||
sane.programs.zeal = {
|
||||
package = pkgs.zeal-qt5;
|
||||
persist.plaintext = [
|
||||
".cache/Zeal"
|
||||
".local/share/Zeal"
|
||||
];
|
||||
fs.".local/share/Zeal/Zeal/system" = sane-lib.fs.wantedSymlinkTo "/run/current-system/sw/share/docset";
|
||||
fs.".local/share/Zeal/Zeal/docsets/system".symlink.target = "/run/current-system/sw/share/docset";
|
||||
suggestedPrograms = [ "docsets" ];
|
||||
};
|
||||
|
||||
environment.pathsToLink = mkIf config.sane.programs.zeal-qt5.enabled [
|
||||
sane.programs.docsets = {
|
||||
configOption = mkOption {
|
||||
type = configOpts;
|
||||
default = {};
|
||||
};
|
||||
package = pkgs.symlinkJoin {
|
||||
name = "docsets";
|
||||
# build each package with rust docs
|
||||
paths = map (name:
|
||||
let
|
||||
orig = pkgs."${name}";
|
||||
withDocs = orig.overrideAttrs (upstream: {
|
||||
nativeBuildInputs = upstream.nativeBuildInputs or [] ++ [
|
||||
pkgs.cargoDocsetHook
|
||||
];
|
||||
});
|
||||
in
|
||||
"${toString withDocs}/share/docset"
|
||||
) cfg.rustPkgs;
|
||||
# link only the docs (not any binaries)
|
||||
postBuild = optionalString (cfg.rustPkgs != []) ''
|
||||
mkdir -p $out/share/docset
|
||||
mv $out/*.docset $out/share/docset
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
environment.pathsToLink = mkIf config.sane.programs.zeal.enabled [
|
||||
"/share/docset"
|
||||
];
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
inherit (lib) mkIf mkMerge mkOption types;
|
||||
@@ -51,12 +51,12 @@ in
|
||||
];
|
||||
|
||||
# zsh/prezto complains if zshrc doesn't exist; but it does allow an "empty" file.
|
||||
fs.".config/zsh/.zshrc" = sane-lib.fs.wantedText "# ";
|
||||
fs.".config/zsh/.zshrc".symlink.text = "# ";
|
||||
|
||||
# prezto = oh-my-zsh fork; controls prompt, auto-completion, etc.
|
||||
# see: https://github.com/sorin-ionescu/prezto
|
||||
# i believe this file is auto-sourced by the prezto init.zsh script.
|
||||
fs.".config/zsh/.zpreztorc" = sane-lib.fs.wantedText ''
|
||||
fs.".config/zsh/.zpreztorc".symlink.text = ''
|
||||
zstyle ':prezto:*:*' color 'yes'
|
||||
|
||||
# modules (they ship with prezto):
|
||||
@@ -138,7 +138,7 @@ in
|
||||
}
|
||||
''
|
||||
+ lib.optionalString cfg.showDeadlines ''
|
||||
${pkgs.sane-scripts}/bin/sane-deadlines
|
||||
${pkgs.sane-scripts.deadlines}/bin/sane-deadlines
|
||||
''
|
||||
+ ''
|
||||
# auto-cd into any of these dirs by typing them and pressing 'enter':
|
||||
|
@@ -1,38 +1,49 @@
|
||||
{ config, ... }:
|
||||
# SOPS configuration:
|
||||
# docs: https://github.com/Mic92/sops-nix
|
||||
#
|
||||
# for each new user you want to edit sops files:
|
||||
# create a private age key from ssh key:
|
||||
# $ mkdir -p ~/.config/sops/age; ssh-to-age -private-key -i ~/.ssh/id_ed25519 > ~/.config/sops/age/keys.txt; chmod 600 ~/.config/sops/age/keys.txt
|
||||
# if the private key was password protected, then first decrypt it:
|
||||
# $ cp ~/.ssh/id_ed25519 /tmp/id_ed25519
|
||||
# $ ssh-keygen -p -N "" -f /tmp/id_ed25519
|
||||
#
|
||||
# for each user you want to decrypt secrets:
|
||||
# $ cat ~/.ssh/id_ed25519.pub | ssh-to-age
|
||||
# add the result to .sops.yaml
|
||||
# since we specify ssh pubkeys in the nix config, you can just grep for `ssh-ed25519` here and use those instead
|
||||
#
|
||||
# for each host you want to decrypt secrets:
|
||||
# $ cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age
|
||||
# add the result to .sops.yaml
|
||||
# $ sops updatekeys secrets/example.yaml
|
||||
#
|
||||
# to create a new secret:
|
||||
# $ sops secrets/example.yaml
|
||||
# control access below (sops.secret.<x>.owner = ...)
|
||||
#
|
||||
# to read a secret:
|
||||
# $ cat /run/secrets/example_key
|
||||
|
||||
{ config, lib, sane-lib, ... }:
|
||||
|
||||
let
|
||||
inherit (lib.strings) hasSuffix removeSuffix;
|
||||
secretsForHost = host: sane-lib.joinAttrsets (
|
||||
map
|
||||
(path: lib.optionalAttrs (hasSuffix ".bin" path) (sane-lib.nameValueToAttrs {
|
||||
name = removeSuffix ".bin" path;
|
||||
value = {
|
||||
sopsFile = ../../secrets/${host}/${path};
|
||||
format = "binary";
|
||||
};
|
||||
}))
|
||||
(sane-lib.enumerateFilePaths ../../secrets/${host})
|
||||
);
|
||||
in
|
||||
{
|
||||
# SOPS configuration:
|
||||
# docs: https://github.com/Mic92/sops-nix
|
||||
#
|
||||
# for each new user you want to edit sops files:
|
||||
# create a private age key from ssh key:
|
||||
# $ mkdir -p ~/.config/sops/age; ssh-to-age -private-key -i ~/.ssh/id_ed25519 > ~/.config/sops/age/keys.txt; chmod 600 ~/.config/sops/age/keys.txt
|
||||
# if the private key was password protected, then first decrypt it:
|
||||
# $ cp ~/.ssh/id_ed25519 /tmp/id_ed25519
|
||||
# $ ssh-keygen -p -N "" -f /tmp/id_ed25519
|
||||
#
|
||||
# for each user you want to decrypt secrets:
|
||||
# $ cat ~/.ssh/id_ed25519.pub | ssh-to-age
|
||||
# add the result to .sops.yaml
|
||||
# since we specify ssh pubkeys in the nix config, you can just grep for `ssh-ed25519` here and use those instead
|
||||
#
|
||||
# for each host you want to decrypt secrets:
|
||||
# $ cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age
|
||||
# add the result to .sops.yaml
|
||||
# $ sops updatekeys secrets/example.yaml
|
||||
#
|
||||
# to create a new secret:
|
||||
# $ sops secrets/example.yaml
|
||||
# control access below (sops.secret.<x>.owner = ...)
|
||||
#
|
||||
# to read a secret:
|
||||
# $ cat /run/secrets/example_key
|
||||
|
||||
# sops.age.sshKeyPaths = [ "/home/colin/.ssh/id_ed25519_dec" ];
|
||||
# This will add secrets.yaml to the nix store
|
||||
# You can avoid this by adding a string to the full path instead, i.e.
|
||||
# sops.defaultSopsFile = "/root/.sops/secrets/example.yaml";
|
||||
sops.defaultSopsFile = ../../secrets/universal.yaml;
|
||||
sops.gnupg.sshKeyPaths = []; # disable RSA key import
|
||||
# This is using an age key that is expected to already be in the filesystem
|
||||
# sops.age.keyFile = "/home/colin/.ssh/age.pub";
|
||||
@@ -45,96 +56,15 @@
|
||||
# };
|
||||
# sops.secrets."myservice/my_subdir/my_secret" = {};
|
||||
|
||||
## universal secrets
|
||||
# TODO: glob these?
|
||||
|
||||
sops.secrets."jackett_apikey" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
owner = config.users.users.colin.name;
|
||||
};
|
||||
sops.secrets."mx-sanebot-env" = {
|
||||
sopsFile = ../../secrets/universal/mx-sanebot-env.bin;
|
||||
format = "binary";
|
||||
owner = config.users.users.colin.name;
|
||||
};
|
||||
sops.secrets."router_passwd" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
};
|
||||
sops.secrets."transmission_passwd" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
};
|
||||
sops.secrets."wg_ovpnd_us_privkey" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
};
|
||||
sops.secrets."wg_ovpnd_us-atl_privkey" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
};
|
||||
sops.secrets."wg_ovpnd_us-mi_privkey" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
};
|
||||
sops.secrets."wg_ovpnd_ukr_privkey" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."snippets" = {
|
||||
sopsFile = ../../secrets/universal/snippets.bin;
|
||||
format = "binary";
|
||||
owner = config.users.users.colin.name;
|
||||
};
|
||||
|
||||
sops.secrets."bt/car" = {
|
||||
sopsFile = ../../secrets/universal/bt/car.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."bt/earbuds" = {
|
||||
sopsFile = ../../secrets/universal/bt/earbuds.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."bt/portable-speaker" = {
|
||||
sopsFile = ../../secrets/universal/bt/portable-speaker.bin;
|
||||
format = "binary";
|
||||
};
|
||||
|
||||
sops.secrets."iwd/community-university.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/community-university.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/friend-libertarian-dod.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/friend-libertarian-dod.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/friend-rationalist-empathist.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/friend-rationalist-empathist.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/home-shared.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/home-shared.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/makespace-south.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/makespace-south.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/archive-2023-02-home-bedroom.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/archive/2023-02-home-bedroom.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/archive-2023-02-home-shared-24G.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/archive/2023-02-home-shared-24G.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/archive-2023-02-home-shared.psk" = {
|
||||
sopsFile = ../../secrets/universal/net/archive/2023-02-home-shared.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/iphone" = {
|
||||
sopsFile = ../../secrets/universal/net/iphone.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets."iwd/parents" = {
|
||||
sopsFile = ../../secrets/universal/net/parents.psk.bin;
|
||||
format = "binary";
|
||||
};
|
||||
sops.secrets = lib.mkMerge [
|
||||
(secretsForHost "common")
|
||||
(secretsForHost config.networking.hostName)
|
||||
{
|
||||
"jackett_apikey".owner = config.users.users.colin.name;
|
||||
"mx-sanebot-env".owner = config.users.users.colin.name;
|
||||
"snippets".owner = config.users.users.colin.name;
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
@@ -30,4 +30,15 @@ in
|
||||
})
|
||||
(globalKeys ++ domainKeys)
|
||||
);
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings.PermitRootLogin = "no";
|
||||
settings.PasswordAuthentication = false;
|
||||
};
|
||||
sane.ports.ports."22" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
description = lib.mkDefault "colin-ssh";
|
||||
};
|
||||
}
|
||||
|
@@ -33,13 +33,14 @@ in
|
||||
];
|
||||
group = "users";
|
||||
extraGroups = [
|
||||
"wheel"
|
||||
"nixbuild"
|
||||
"networkmanager"
|
||||
# phosh/mobile. XXX colin: unsure if necessary
|
||||
"video"
|
||||
"dialout" # required for modem access (moby)
|
||||
"feedbackd"
|
||||
"dialout" # required for modem access
|
||||
"input" # for /dev/input/<xyz>: sxmo
|
||||
"networkmanager"
|
||||
"nixbuild"
|
||||
"video" # phosh/mobile. XXX colin: unsure if necessary
|
||||
"wheel"
|
||||
"wireshark"
|
||||
];
|
||||
|
||||
# initial password is empty, in case anything goes wrong.
|
||||
@@ -128,11 +129,5 @@ in
|
||||
enable = true;
|
||||
wheelNeedsPassword = false;
|
||||
};
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings.PermitRootLogin = "no";
|
||||
settings.PasswordAuthentication = false;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@ let
|
||||
def-ovpn = name: { endpoint, publicKey, address }: {
|
||||
networking.wg-quick.interfaces."ovpnd-${name}" = {
|
||||
inherit address;
|
||||
privateKeyFile = config.sops.secrets."wg_ovpnd_${name}_privkey".path;
|
||||
privateKeyFile = config.sops.secrets."wg/ovpnd_${name}_privkey".path;
|
||||
dns = [
|
||||
"46.227.67.134"
|
||||
"192.165.9.158"
|
||||
|
@@ -4,7 +4,7 @@
|
||||
{ hostName, localSystem }:
|
||||
|
||||
# module args
|
||||
{ config, lib, ... }:
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
@@ -15,15 +15,4 @@
|
||||
|
||||
networking.hostName = hostName;
|
||||
nixpkgs.buildPlatform = lib.mkIf (localSystem != null) localSystem;
|
||||
sane.cross.enablePatches = localSystem != null;
|
||||
|
||||
# nixpkgs.overlays = [
|
||||
# (next: prev: {
|
||||
# # for local != target we by default just emulate the target while building.
|
||||
# # provide a `pkgs.cross.<pkg>` alias that consumers can use instead of `pkgs.<foo>`
|
||||
# # to explicitly opt into non-emulated cross compilation for any specific package.
|
||||
# # this is most beneficial for large packages with few pre-requisites -- like Linux.
|
||||
# cross = prev.crossFrom."${localSystem}";
|
||||
# })
|
||||
# ];
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ in
|
||||
./phosh.nix
|
||||
./plasma.nix
|
||||
./plasma-mobile.nix
|
||||
./sway.nix
|
||||
./sway
|
||||
./sxmo.nix
|
||||
];
|
||||
}
|
||||
|
@@ -59,6 +59,8 @@ in
|
||||
# qt.style = "gtk2";
|
||||
|
||||
# docs: https://github.com/NixOS/nixpkgs/blob/nixos-22.05/nixos/modules/services/x11/desktop-managers/phosh.nix
|
||||
# docs: <repo:gnome/phosh:src/phoc.ini.example>
|
||||
# docs: <repo:gnome/phosh:src/settings.c#config_ini_handler>
|
||||
services.xserver.desktopManager.phosh = {
|
||||
enable = true;
|
||||
user = "colin";
|
||||
@@ -113,6 +115,18 @@ in
|
||||
NIXOS_OZONE_WL = "1";
|
||||
};
|
||||
|
||||
systemd.services.phosh.environment = {
|
||||
# PHOC_DEBUG: comma-separated list of:
|
||||
# - ``auto-maximize``: Maximize toplevels
|
||||
# - ``damage-tracking``: Debug damage tracking
|
||||
# - ``no-quit``: Don't quit when session ends
|
||||
# - ``touch-points``: Debug touch points
|
||||
# - ``layer-shell``: Debug layer shell
|
||||
# - ``cutouts``: Debug display cutouts and notches
|
||||
PHOC_DEBUG = "layer-shell";
|
||||
# G_DEBUG, G_MESSAGE_DEBUG for glib debugging: <https://docs.gtk.org/glib/running.html>
|
||||
};
|
||||
|
||||
programs.dconf.packages = [
|
||||
# org.kde.konsole.desktop
|
||||
(pkgs.writeTextFile {
|
||||
@@ -142,7 +156,11 @@ in
|
||||
services.xserver.displayManager.job.preStart = ''
|
||||
${pkgs.systemd}/bin/busctl call org.freedesktop.Accounts /org/freedesktop/Accounts org.freedesktop.Accounts CacheUser s colin
|
||||
'';
|
||||
# services.xserver.displayManager.defaultSession = "sm.puri.Phosh"; # XXX: not sure why this doesn't propagate correctly.
|
||||
# XXX for some reason specifying defaultSession = "sm.puri.Phosh" breaks cross-compiled display-manager startup
|
||||
# - causes an attempt to load x86-64 glib-2.76.2/lib/libglib-2.0.so.0
|
||||
# - likely <repo:nixpkgs:nixos/modules/services/x11/display-managers/account-service-util.nix>
|
||||
# - but i believe some variant of this issue existed even during emulated compilation
|
||||
# services.xserver.displayManager.defaultSession = "sm.puri.Phosh";
|
||||
services.xserver.displayManager.lightdm.extraSeatDefaults = ''
|
||||
user-session = phosh
|
||||
'';
|
||||
|
@@ -10,4 +10,6 @@ https://jackett.uninsane.org/UI/Dashboard#search=
|
||||
https://fed.uninsane.org
|
||||
https://bt.uninsane.org
|
||||
https://sci-hub.se
|
||||
https://archive.is
|
||||
https://news.ycombinator.com
|
||||
https://192.168.15.1:60481 # Router/Firewall
|
||||
|
@@ -1,666 +0,0 @@
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
|
||||
# docs: https://nixos.wiki/wiki/Sway
|
||||
with lib;
|
||||
let
|
||||
cfg = config.sane.gui.sway;
|
||||
# docs: https://github.com/Alexays/Waybar/wiki/Configuration
|
||||
# format specifiers: https://fmt.dev/latest/syntax.html#syntax
|
||||
waybar-config = [
|
||||
{ # TOP BAR
|
||||
layer = "top";
|
||||
height = 40;
|
||||
modules-left = ["sway/workspaces" "sway/mode"];
|
||||
modules-center = ["sway/window"];
|
||||
modules-right = ["custom/mediaplayer" "clock" "battery" "cpu" "network"];
|
||||
"sway/window" = {
|
||||
max-length = 50;
|
||||
};
|
||||
# include song artist/title. source: https://www.reddit.com/r/swaywm/comments/ni0vso/waybar_spotify_tracktitle/
|
||||
"custom/mediaplayer" = {
|
||||
exec = pkgs.writeShellScript "waybar-mediaplayer" ''
|
||||
player_status=$(${pkgs.playerctl}/bin/playerctl status 2> /dev/null)
|
||||
if [ "$player_status" = "Playing" ]; then
|
||||
echo "$(${pkgs.playerctl}/bin/playerctl metadata artist) - $(${pkgs.playerctl}/bin/playerctl metadata title)"
|
||||
elif [ "$player_status" = "Paused" ]; then
|
||||
echo " $(${pkgs.playerctl}/bin/playerctl metadata artist) - $(${pkgs.playerctl}/bin/playerctl metadata title)"
|
||||
fi
|
||||
'';
|
||||
interval = 2;
|
||||
format = "{} ";
|
||||
# return-type = "json";
|
||||
on-click = "${pkgs.playerctl}/bin/playerctl play-pause";
|
||||
on-scroll-up = "${pkgs.playerctl}/bin/playerctl next";
|
||||
on-scroll-down = "${pkgs.playerctl}/bin/playerctl previous";
|
||||
};
|
||||
network = {
|
||||
# docs: https://github.com/Alexays/Waybar/blob/master/man/waybar-network.5.scd
|
||||
interval = 2;
|
||||
max-length = 40;
|
||||
# custom :> format specifier explained here: https://github.com/Alexays/Waybar/pull/472
|
||||
format-ethernet = " {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
||||
tooltip-format-ethernet = "{ifname} {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
||||
|
||||
format-wifi = "{ifname} ({signalStrength}%) {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
||||
tooltip-format-wifi = "{essid} ({signalStrength}%) {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
||||
|
||||
format-disconnected = "";
|
||||
};
|
||||
cpu = {
|
||||
format = " {usage:2}%";
|
||||
tooltip = false;
|
||||
};
|
||||
battery = {
|
||||
states = {
|
||||
good = 95;
|
||||
warning = 30;
|
||||
critical = 10;
|
||||
};
|
||||
format = "{icon} {capacity}%";
|
||||
format-icons = [
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
];
|
||||
};
|
||||
clock = {
|
||||
format-alt = "{:%a, %d. %b %H:%M}";
|
||||
};
|
||||
}
|
||||
];
|
||||
# waybar-config-text = lib.generators.toJSON {} waybar-config;
|
||||
waybar-config-text = (pkgs.formats.json {}).generate "waybar-config.json" waybar-config;
|
||||
|
||||
# bare sway launcher
|
||||
sway-launcher = pkgs.writeShellScriptBin "sway-launcher" ''
|
||||
${pkgs.sway}/bin/sway --debug > /tmp/sway.log 2>&1
|
||||
'';
|
||||
# start sway and have it construct the gtkgreeter
|
||||
sway-as-greeter = pkgs.writeShellScriptBin "sway-as-greeter" ''
|
||||
${pkgs.sway}/bin/sway --debug --config ${sway-config-into-gtkgreet} > /tmp/sway-as-greeter.log 2>&1
|
||||
'';
|
||||
# (config file for the above)
|
||||
sway-config-into-gtkgreet = pkgs.writeText "greetd-sway-config" ''
|
||||
exec "${gtkgreet-launcher}"
|
||||
'';
|
||||
# gtkgreet which launches a layered sway instance
|
||||
gtkgreet-launcher = pkgs.writeShellScript "gtkgreet-launcher" ''
|
||||
# NB: the "command" field here is run in the user's shell.
|
||||
# so that command must exist on the specific user's path who is logging in. it doesn't need to exist system-wide.
|
||||
${pkgs.greetd.gtkgreet}/bin/gtkgreet --layer-shell --command sway-launcher
|
||||
'';
|
||||
greeter-session = {
|
||||
# greeter session config
|
||||
command = "${sway-as-greeter}/bin/sway-as-greeter";
|
||||
# alternatives:
|
||||
# - TTY: `command = "${pkgs.greetd.greetd}/bin/agreety --cmd ${pkgs.sway}/bin/sway";`
|
||||
# - autologin: `command = "${pkgs.sway}/bin/sway"; user = "colin";`
|
||||
# - Dumb Login (doesn't work)": `command = "${pkgs.greetd.dlm}/bin/dlm";`
|
||||
};
|
||||
greeterless-session = {
|
||||
# no greeter
|
||||
command = "${sway-launcher}/bin/sway-launcher";
|
||||
user = "colin";
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sane.gui.sway.enable = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
};
|
||||
sane.gui.sway.useGreeter = mkOption {
|
||||
description = ''
|
||||
launch sway via a greeter (like greetd's gtkgreet).
|
||||
sway is usable without a greeter, but skipping the greeter means no PAM session.
|
||||
'';
|
||||
default = true;
|
||||
type = types.bool;
|
||||
};
|
||||
};
|
||||
config = mkMerge [
|
||||
{
|
||||
sane.programs.swayApps = {
|
||||
package = null;
|
||||
suggestedPrograms = [
|
||||
"guiApps"
|
||||
"splatmoji" # used by us, but 'enabling' it gets us persistence & cfg
|
||||
"swaylock"
|
||||
"swayidle"
|
||||
"wl-clipboard"
|
||||
"mako" # notification daemon
|
||||
# # "pavucontrol"
|
||||
"gnome.gnome-bluetooth"
|
||||
"gnome.gnome-control-center"
|
||||
"sway-contrib.grimshot"
|
||||
];
|
||||
};
|
||||
}
|
||||
{
|
||||
sane.programs = {
|
||||
inherit (pkgs // {
|
||||
"gnome.gnome-bluetooth" = pkgs.gnome.gnome-bluetooth;
|
||||
"gnome.gnome-control-center" = pkgs.gnome.gnome-control-center;
|
||||
"sway-contrib.grimshot" = pkgs.sway-contrib.grimshot;
|
||||
})
|
||||
swaylock
|
||||
swayidle
|
||||
wl-clipboard
|
||||
mako
|
||||
"gnome.gnome-bluetooth"
|
||||
"gnome.gnome-control-center"
|
||||
"sway-contrib.grimshot"
|
||||
;
|
||||
};
|
||||
}
|
||||
|
||||
(mkIf cfg.enable {
|
||||
sane.programs.swayApps.enableFor.user.colin = true;
|
||||
|
||||
# swap in these lines to use SDDM instead of `services.greetd`.
|
||||
# services.xserver.displayManager.sddm.enable = true;
|
||||
# services.xserver.enable = true;
|
||||
services.greetd = {
|
||||
# greetd source/docs:
|
||||
# - <https://git.sr.ht/~kennylevinsen/greetd>
|
||||
enable = true;
|
||||
settings = {
|
||||
default_session = if cfg.useGreeter then greeter-session else greeterless-session;
|
||||
};
|
||||
};
|
||||
# we need the greeter's command to be on our PATH
|
||||
users.users.colin.packages = [ sway-launcher ];
|
||||
|
||||
# some programs (e.g. fractal) **require** a "Secret Service Provider"
|
||||
services.gnome.gnome-keyring.enable = true;
|
||||
|
||||
# unlike other DEs, sway configures no audio stack
|
||||
# administer with pw-cli, pw-mon, pw-top commands
|
||||
services.pipewire = {
|
||||
enable = true;
|
||||
alsa.enable = true;
|
||||
alsa.support32Bit = true; # ??
|
||||
pulse.enable = true;
|
||||
};
|
||||
|
||||
networking.useDHCP = false;
|
||||
networking.networkmanager.enable = true;
|
||||
networking.wireless.enable = lib.mkForce false;
|
||||
|
||||
hardware.bluetooth.enable = true;
|
||||
services.blueman.enable = true;
|
||||
# gsd provides Rfkill, which is required for the bluetooth pane in gnome-control-center to work
|
||||
services.gnome.gnome-settings-daemon.enable = true;
|
||||
# start the components of gsd we need at login
|
||||
systemd.user.targets."org.gnome.SettingsDaemon.Rfkill".wantedBy = [ "graphical-session.target" ];
|
||||
# go ahead and `systemctl --user cat gnome-session-initialized.target`. i dare you.
|
||||
# the only way i can figure out how to get Rfkill to actually load is to just disable all the shit it depends on.
|
||||
# it doesn't actually seem to need ANY of them in the first place T_T
|
||||
systemd.user.targets."gnome-session-initialized".enable = false;
|
||||
# bluez can't connect to audio devices unless pipewire is running.
|
||||
# a system service can't depend on a user service, so just launch it at graphical-session
|
||||
systemd.user.services."pipewire".wantedBy = [ "graphical-session.target" ];
|
||||
|
||||
programs.sway = {
|
||||
enable = true;
|
||||
wrapperFeatures.gtk = true;
|
||||
};
|
||||
sane.user.fs.".config/sway/config" =
|
||||
let
|
||||
fuzzel = "${pkgs.fuzzel}/bin/fuzzel";
|
||||
sed = "${pkgs.gnused}/bin/sed";
|
||||
wtype = "${pkgs.wtype}/bin/wtype";
|
||||
kitty = "${pkgs.kitty}/bin/kitty";
|
||||
launcher-cmd = fuzzel;
|
||||
terminal-cmd = kitty;
|
||||
lock-cmd = "${pkgs.swaylock}/bin/swaylock --indicator-idle-visible --indicator-radius 100 --indicator-thickness 30";
|
||||
vol-up-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume +5";
|
||||
vol-down-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume -5";
|
||||
mute-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --toggle-mute";
|
||||
brightness-up-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set +2%";
|
||||
brightness-down-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set 2%-";
|
||||
screenshot-cmd = "${pkgs.sway-contrib.grimshot}/bin/grimshot copy area";
|
||||
# "bookmarking"/snippets inspired by Luke Smith:
|
||||
# - <https://www.youtube.com/watch?v=d_11QaTlf1I>
|
||||
snip-file = ./snippets.txt;
|
||||
# TODO: querying sops here breaks encapsulation
|
||||
list-snips = "cat ${snip-file} ${config.sops.secrets.snippets.path}";
|
||||
strip-comments = "${sed} 's/ #.*$//'";
|
||||
snip-cmd = "${wtype} $(${list-snips} | ${fuzzel} -d -i -w 60 | ${strip-comments})";
|
||||
# TODO: next splatmoji release should allow `-s none` to disable skin tones
|
||||
emoji-cmd = "${pkgs.splatmoji}/bin/splatmoji -s medium-light type";
|
||||
in sane-lib.fs.wantedText ''
|
||||
### default font
|
||||
font pango:monospace 8
|
||||
|
||||
### pixel boundary between windows
|
||||
default_border pixel 3
|
||||
default_floating_border pixel 2
|
||||
hide_edge_borders smart
|
||||
|
||||
### defaults
|
||||
focus_wrapping no
|
||||
focus_follows_mouse yes
|
||||
focus_on_window_activation smart
|
||||
mouse_warping output
|
||||
workspace_layout default
|
||||
workspace_auto_back_and_forth no
|
||||
|
||||
### default colors (#border #background #text #indicator #childBorder)
|
||||
client.focused #4c7899 #285577 #ffffff #2e9ef4 #285577
|
||||
client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
|
||||
client.unfocused #333333 #222222 #888888 #292d2e #222222
|
||||
client.urgent #2f343a #900000 #ffffff #900000 #900000
|
||||
client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
|
||||
client.background #ffffff
|
||||
|
||||
### key bindings
|
||||
floating_modifier Mod1
|
||||
## media keys
|
||||
bindsym XF86AudioRaiseVolume exec ${vol-up-cmd}
|
||||
bindsym XF86AudioLowerVolume exec ${vol-down-cmd}
|
||||
bindsym Mod1+Page_Up exec ${vol-up-cmd}
|
||||
bindsym Mod1+Page_Down exec ${vol-down-cmd}
|
||||
bindsym XF86AudioMute exec ${mute-cmd}
|
||||
bindsym XF86MonBrightnessUp exec ${brightness-up-cmd}
|
||||
bindsym XF86MonBrightnessDown exec ${brightness-down-cmd}
|
||||
## special functions
|
||||
bindsym Mod1+Print exec ${screenshot-cmd}
|
||||
bindsym Mod1+l exec ${lock-cmd}
|
||||
bindsym Mod1+s exec ${snip-cmd}
|
||||
bindsym Mod1+slash exec ${emoji-cmd}
|
||||
bindsym Mod1+d exec ${launcher-cmd}
|
||||
bindsym Mod1+Return exec ${terminal-cmd}
|
||||
bindsym Mod1+Shift+q kill
|
||||
bindsym Mod1+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'
|
||||
bindsym Mod1+Shift+c reload
|
||||
## layout
|
||||
bindsym Mod1+b splith
|
||||
bindsym Mod1+v splitv
|
||||
bindsym Mod1+f fullscreen toggle
|
||||
bindsym Mod1+a focus parent
|
||||
bindsym Mod1+w layout tabbed
|
||||
bindsym Mod1+e layout toggle split
|
||||
bindsym Mod1+Shift+space floating toggle
|
||||
bindsym Mod1+space focus mode_toggle
|
||||
bindsym Mod1+r mode resize
|
||||
## movement
|
||||
bindsym Mod1+Up focus up
|
||||
bindsym Mod1+Down focus down
|
||||
bindsym Mod1+Left focus left
|
||||
bindsym Mod1+Right focus right
|
||||
bindsym Mod1+Shift+Up move up
|
||||
bindsym Mod1+Shift+Down move down
|
||||
bindsym Mod1+Shift+Left move left
|
||||
bindsym Mod1+Shift+Right move right
|
||||
## workspaces
|
||||
bindsym Mod1+1 workspace number 1
|
||||
bindsym Mod1+2 workspace number 2
|
||||
bindsym Mod1+3 workspace number 3
|
||||
bindsym Mod1+4 workspace number 4
|
||||
bindsym Mod1+5 workspace number 5
|
||||
bindsym Mod1+6 workspace number 6
|
||||
bindsym Mod1+7 workspace number 7
|
||||
bindsym Mod1+8 workspace number 8
|
||||
bindsym Mod1+9 workspace number 9
|
||||
bindsym Mod1+Shift+1 move container to workspace number 1
|
||||
bindsym Mod1+Shift+2 move container to workspace number 2
|
||||
bindsym Mod1+Shift+3 move container to workspace number 3
|
||||
bindsym Mod1+Shift+4 move container to workspace number 4
|
||||
bindsym Mod1+Shift+5 move container to workspace number 5
|
||||
bindsym Mod1+Shift+6 move container to workspace number 6
|
||||
bindsym Mod1+Shift+7 move container to workspace number 7
|
||||
bindsym Mod1+Shift+8 move container to workspace number 8
|
||||
bindsym Mod1+Shift+9 move container to workspace number 9
|
||||
## "scratchpad" = ??
|
||||
bindsym Mod1+Shift+minus move scratchpad
|
||||
bindsym Mod1+minus scratchpad show
|
||||
|
||||
### defaults
|
||||
mode "resize" {
|
||||
bindsym Down resize grow height 10 px
|
||||
bindsym Escape mode default
|
||||
bindsym Left resize shrink width 10 px
|
||||
bindsym Return mode default
|
||||
bindsym Right resize grow width 10 px
|
||||
bindsym Up resize shrink height 10 px
|
||||
bindsym h resize shrink width 10 px
|
||||
bindsym j resize grow height 10 px
|
||||
bindsym k resize shrink height 10 px
|
||||
bindsym l resize grow width 10 px
|
||||
}
|
||||
|
||||
### lightly modified bars
|
||||
bar {
|
||||
# TODO: fonts was:
|
||||
# config.fonts.fontconfig.defaultFonts; (monospace ++ emoji)
|
||||
font pango:Hack, Font Awesome 6 Free, Twitter Color Emoji 24.000000
|
||||
mode dock
|
||||
hidden_state hide
|
||||
position top
|
||||
status_command ${pkgs.i3status}/bin/i3status
|
||||
swaybar_command ${pkgs.waybar}/bin/waybar
|
||||
workspace_buttons yes
|
||||
strip_workspace_numbers no
|
||||
tray_output primary
|
||||
colors {
|
||||
background #000000
|
||||
statusline #ffffff
|
||||
separator #666666
|
||||
# #border #background #text
|
||||
focused_workspace #4c7899 #285577 #ffffff
|
||||
active_workspace #333333 #5f676a #ffffff
|
||||
inactive_workspace #333333 #222222 #888888
|
||||
urgent_workspace #2f343a #900000 #ffffff
|
||||
binding_mode #2f343a #900000 #ffffff
|
||||
}
|
||||
}
|
||||
|
||||
### displays
|
||||
## DESKTOP
|
||||
output "Samsung Electric Company S22C300 0x00007F35" {
|
||||
pos 0,0
|
||||
res 1920x1080
|
||||
}
|
||||
output "Goldstar Company Ltd LG ULTRAWIDE 0x00004E94" {
|
||||
pos 1920,0
|
||||
res 3440x1440
|
||||
}
|
||||
|
||||
## LAPTOP
|
||||
# sh/en TV
|
||||
output "Pioneer Electronic Corporation VSX-524 0x00000101" {
|
||||
pos 0,0
|
||||
res 1920x1080
|
||||
}
|
||||
# internal display
|
||||
output "Unknown 0x0637 0x00000000" {
|
||||
pos 1920,0
|
||||
res 1920x1080
|
||||
}
|
||||
'';
|
||||
|
||||
sane.user.fs.".config/waybar/config" = sane-lib.fs.wantedSymlinkTo waybar-config-text;
|
||||
|
||||
# style docs: https://github.com/Alexays/Waybar/wiki/Styling
|
||||
sane.user.fs.".config/waybar/style.css" = sane-lib.fs.wantedText ''
|
||||
* {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
/* defaults below: https://github.com/Alexays/Waybar/blob/master/resources/style.css */
|
||||
window#waybar {
|
||||
background-color: rgba(43, 48, 59, 0.5);
|
||||
border-bottom: 3px solid rgba(100, 114, 125, 0.5);
|
||||
color: #ffffff;
|
||||
transition-property: background-color;
|
||||
transition-duration: .5s;
|
||||
}
|
||||
|
||||
window#waybar.hidden {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
/*
|
||||
window#waybar.empty {
|
||||
background-color: transparent;
|
||||
}
|
||||
window#waybar.solo {
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
*/
|
||||
|
||||
window#waybar.termite {
|
||||
background-color: #3F3F3F;
|
||||
}
|
||||
|
||||
window#waybar.chromium {
|
||||
background-color: #000000;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#workspaces button {
|
||||
padding: 0 5px;
|
||||
background-color: transparent;
|
||||
color: #ffffff;
|
||||
/* Use box-shadow instead of border so the text isn't offset */
|
||||
box-shadow: inset 0 -3px transparent;
|
||||
/* Avoid rounded borders under each workspace name */
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
|
||||
#workspaces button:hover {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
box-shadow: inset 0 -3px #ffffff;
|
||||
}
|
||||
|
||||
#workspaces button.focused {
|
||||
background-color: #64727D;
|
||||
box-shadow: inset 0 -3px #ffffff;
|
||||
}
|
||||
|
||||
#workspaces button.urgent {
|
||||
background-color: #eb4d4b;
|
||||
}
|
||||
|
||||
#mode {
|
||||
background-color: #64727D;
|
||||
border-bottom: 3px solid #ffffff;
|
||||
}
|
||||
|
||||
#clock,
|
||||
#battery,
|
||||
#cpu,
|
||||
#memory,
|
||||
#disk,
|
||||
#temperature,
|
||||
#backlight,
|
||||
#network,
|
||||
#pulseaudio,
|
||||
#custom-media,
|
||||
#tray,
|
||||
#mode,
|
||||
#idle_inhibitor,
|
||||
#mpd {
|
||||
padding: 0 10px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#window,
|
||||
#workspaces {
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
/* If workspaces is the leftmost module, omit left margin */
|
||||
.modules-left > widget:first-child > #workspaces {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
/* If workspaces is the rightmost module, omit right margin */
|
||||
.modules-right > widget:last-child > #workspaces {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
#clock {
|
||||
background-color: #64727D;
|
||||
}
|
||||
|
||||
#battery {
|
||||
background-color: #ffffff;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
#battery.charging, #battery.plugged {
|
||||
color: #ffffff;
|
||||
background-color: #26A65B;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
to {
|
||||
background-color: #ffffff;
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
|
||||
#battery.critical:not(.charging) {
|
||||
background-color: #f53c3c;
|
||||
color: #ffffff;
|
||||
animation-name: blink;
|
||||
animation-duration: 0.5s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
}
|
||||
|
||||
label:focus {
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
#cpu {
|
||||
background-color: #2ecc71;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
#memory {
|
||||
background-color: #9b59b6;
|
||||
}
|
||||
|
||||
#disk {
|
||||
background-color: #964B00;
|
||||
}
|
||||
|
||||
#backlight {
|
||||
background-color: #90b1b1;
|
||||
}
|
||||
|
||||
#network {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
#network.disconnected {
|
||||
background-color: #f53c3c;
|
||||
}
|
||||
|
||||
#pulseaudio {
|
||||
background-color: #f1c40f;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
#pulseaudio.muted {
|
||||
background-color: #90b1b1;
|
||||
color: #2a5c45;
|
||||
}
|
||||
|
||||
#custom-media {
|
||||
background-color: #66cc99;
|
||||
color: #2a5c45;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
#custom-media.custom-spotify {
|
||||
background-color: #66cc99;
|
||||
}
|
||||
|
||||
#custom-media.custom-vlc {
|
||||
background-color: #ffa000;
|
||||
}
|
||||
|
||||
#temperature {
|
||||
background-color: #f0932b;
|
||||
}
|
||||
|
||||
#temperature.critical {
|
||||
background-color: #eb4d4b;
|
||||
}
|
||||
|
||||
#tray {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
#tray > .passive {
|
||||
-gtk-icon-effect: dim;
|
||||
}
|
||||
|
||||
#tray > .needs-attention {
|
||||
-gtk-icon-effect: highlight;
|
||||
background-color: #eb4d4b;
|
||||
}
|
||||
|
||||
#idle_inhibitor {
|
||||
background-color: #2d3436;
|
||||
}
|
||||
|
||||
#idle_inhibitor.activated {
|
||||
background-color: #ecf0f1;
|
||||
color: #2d3436;
|
||||
}
|
||||
|
||||
#mpd {
|
||||
background-color: #66cc99;
|
||||
color: #2a5c45;
|
||||
}
|
||||
|
||||
#mpd.disconnected {
|
||||
background-color: #f53c3c;
|
||||
}
|
||||
|
||||
#mpd.stopped {
|
||||
background-color: #90b1b1;
|
||||
}
|
||||
|
||||
#mpd.paused {
|
||||
background-color: #51a37a;
|
||||
}
|
||||
|
||||
#language {
|
||||
background: #00b093;
|
||||
color: #740864;
|
||||
padding: 0 5px;
|
||||
margin: 0 5px;
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
#keyboard-state {
|
||||
background: #97e1ad;
|
||||
color: #000000;
|
||||
padding: 0 0px;
|
||||
margin: 0 5px;
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
#keyboard-state > label {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
#keyboard-state > label.locked {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
'';
|
||||
# style = ''
|
||||
# * {
|
||||
# border: none;
|
||||
# border-radius: 0;
|
||||
# font-family: Source Code Pro;
|
||||
# }
|
||||
# window#waybar {
|
||||
# background: #16191C;
|
||||
# color: #AAB2BF;
|
||||
# }
|
||||
# #workspaces button {
|
||||
# padding: 0 5px;
|
||||
# }
|
||||
# .custom-spotify {
|
||||
# padding: 0 10px;
|
||||
# margin: 0 4px;
|
||||
# background-color: #1DB954;
|
||||
# color: black;
|
||||
# }
|
||||
# '';
|
||||
})
|
||||
];
|
||||
}
|
||||
|
164
hosts/modules/gui/sway/default.nix
Normal file
164
hosts/modules/gui/sway/default.nix
Normal file
@@ -0,0 +1,164 @@
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
|
||||
# docs: https://nixos.wiki/wiki/Sway
|
||||
with lib;
|
||||
let
|
||||
cfg = config.sane.gui.sway;
|
||||
|
||||
# bare sway launcher
|
||||
sway-launcher = pkgs.writeShellScriptBin "sway-launcher" ''
|
||||
${pkgs.sway}/bin/sway --debug > /var/log/sway/sway.log 2>&1
|
||||
'';
|
||||
# start sway and have it construct the gtkgreeter
|
||||
sway-as-greeter = pkgs.writeShellScriptBin "sway-as-greeter" ''
|
||||
${pkgs.sway}/bin/sway --debug --config ${sway-config-into-gtkgreet} > /var/log/sway/sway-as-greeter.log 2>&1
|
||||
'';
|
||||
# (config file for the above)
|
||||
sway-config-into-gtkgreet = pkgs.writeText "greetd-sway-config" ''
|
||||
exec "${gtkgreet-launcher}"
|
||||
'';
|
||||
# gtkgreet which launches a layered sway instance
|
||||
gtkgreet-launcher = pkgs.writeShellScript "gtkgreet-launcher" ''
|
||||
# NB: the "command" field here is run in the user's shell.
|
||||
# so that command must exist on the specific user's path who is logging in. it doesn't need to exist system-wide.
|
||||
${pkgs.greetd.gtkgreet}/bin/gtkgreet --layer-shell --command sway-launcher
|
||||
'';
|
||||
greeter-session = {
|
||||
# greeter session config
|
||||
command = "${sway-as-greeter}/bin/sway-as-greeter";
|
||||
# alternatives:
|
||||
# - TTY: `command = "${pkgs.greetd.greetd}/bin/agreety --cmd ${pkgs.sway}/bin/sway";`
|
||||
# - autologin: `command = "${pkgs.sway}/bin/sway"; user = "colin";`
|
||||
# - Dumb Login (doesn't work)": `command = "${pkgs.greetd.dlm}/bin/dlm";`
|
||||
};
|
||||
greeterless-session = {
|
||||
# no greeter
|
||||
command = "${sway-launcher}/bin/sway-launcher";
|
||||
user = "colin";
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sane.gui.sway.enable = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
};
|
||||
sane.gui.sway.useGreeter = mkOption {
|
||||
description = ''
|
||||
launch sway via a greeter (like greetd's gtkgreet).
|
||||
sway is usable without a greeter, but skipping the greeter means no PAM session.
|
||||
'';
|
||||
default = true;
|
||||
type = types.bool;
|
||||
};
|
||||
};
|
||||
config = mkMerge [
|
||||
{
|
||||
sane.programs.swayApps = {
|
||||
package = null;
|
||||
suggestedPrograms = [
|
||||
"guiApps"
|
||||
"splatmoji" # used by us, but 'enabling' it gets us persistence & cfg
|
||||
"swaylock"
|
||||
"swayidle"
|
||||
"wl-clipboard"
|
||||
"blueberry" # GUI bluetooth manager
|
||||
"mako" # notification daemon
|
||||
# # "pavucontrol"
|
||||
# "gnome.gnome-bluetooth" # XXX(2023/05/14): broken
|
||||
"gnome.gnome-control-center"
|
||||
"sway-contrib.grimshot"
|
||||
];
|
||||
};
|
||||
}
|
||||
{
|
||||
sane.programs = {
|
||||
inherit (pkgs // {
|
||||
"gnome.gnome-bluetooth" = pkgs.gnome.gnome-bluetooth;
|
||||
"gnome.gnome-control-center" = pkgs.gnome.gnome-control-center;
|
||||
"sway-contrib.grimshot" = pkgs.sway-contrib.grimshot;
|
||||
})
|
||||
swaylock
|
||||
swayidle
|
||||
wl-clipboard
|
||||
blueberry
|
||||
mako
|
||||
"gnome.gnome-bluetooth"
|
||||
"gnome.gnome-control-center"
|
||||
"sway-contrib.grimshot"
|
||||
;
|
||||
};
|
||||
}
|
||||
|
||||
(mkIf cfg.enable {
|
||||
sane.programs.swayApps.enableFor.user.colin = true;
|
||||
|
||||
# swap in these lines to use SDDM instead of `services.greetd`.
|
||||
# services.xserver.displayManager.sddm.enable = true;
|
||||
# services.xserver.enable = true;
|
||||
services.greetd = {
|
||||
# greetd source/docs:
|
||||
# - <https://git.sr.ht/~kennylevinsen/greetd>
|
||||
enable = true;
|
||||
settings = {
|
||||
default_session = if cfg.useGreeter then greeter-session else greeterless-session;
|
||||
};
|
||||
};
|
||||
# we need the greeter's command to be on our PATH
|
||||
users.users.colin.packages = [ sway-launcher ];
|
||||
|
||||
# some programs (e.g. fractal) **require** a "Secret Service Provider"
|
||||
services.gnome.gnome-keyring.enable = true;
|
||||
|
||||
# unlike other DEs, sway configures no audio stack
|
||||
# administer with pw-cli, pw-mon, pw-top commands
|
||||
services.pipewire = {
|
||||
enable = true;
|
||||
alsa.enable = true;
|
||||
alsa.support32Bit = true; # ??
|
||||
pulse.enable = true;
|
||||
};
|
||||
|
||||
networking.useDHCP = false;
|
||||
networking.networkmanager.enable = true;
|
||||
networking.wireless.enable = lib.mkForce false;
|
||||
|
||||
hardware.bluetooth.enable = true;
|
||||
services.blueman.enable = true;
|
||||
# gsd provides Rfkill, which is required for the bluetooth pane in gnome-control-center to work
|
||||
services.gnome.gnome-settings-daemon.enable = true;
|
||||
# start the components of gsd we need at login
|
||||
systemd.user.targets."org.gnome.SettingsDaemon.Rfkill".wantedBy = [ "graphical-session.target" ];
|
||||
# go ahead and `systemctl --user cat gnome-session-initialized.target`. i dare you.
|
||||
# the only way i can figure out how to get Rfkill to actually load is to just disable all the shit it depends on.
|
||||
# it doesn't actually seem to need ANY of them in the first place T_T
|
||||
systemd.user.targets."gnome-session-initialized".enable = false;
|
||||
# bluez can't connect to audio devices unless pipewire is running.
|
||||
# a system service can't depend on a user service, so just launch it at graphical-session
|
||||
systemd.user.services."pipewire".wantedBy = [ "graphical-session.target" ];
|
||||
|
||||
sane.fs."/var/log/sway" = {
|
||||
dir.acl.mode = "0777";
|
||||
wantedBeforeBy = [ "greetd.service" "display-manager.service" ];
|
||||
};
|
||||
|
||||
programs.sway = {
|
||||
enable = true;
|
||||
wrapperFeatures.gtk = true;
|
||||
};
|
||||
sane.user.fs.".config/sway/config" = sane-lib.fs.wantedText
|
||||
(import ./sway-config.nix { inherit config pkgs; });
|
||||
|
||||
sane.user.fs.".config/waybar/config" =
|
||||
let
|
||||
waybar-config = import ./waybar-config.nix { inherit pkgs; };
|
||||
in sane-lib.fs.wantedSymlinkTo (
|
||||
(pkgs.formats.json {}).generate "waybar-config.json" waybar-config
|
||||
);
|
||||
|
||||
sane.user.fs.".config/waybar/style.css" = sane-lib.fs.wantedText
|
||||
(builtins.readFile ./waybar-style.css);
|
||||
})
|
||||
];
|
||||
}
|
||||
|
174
hosts/modules/gui/sway/sway-config.nix
Normal file
174
hosts/modules/gui/sway/sway-config.nix
Normal file
@@ -0,0 +1,174 @@
|
||||
{ pkgs, config }:
|
||||
let
|
||||
fuzzel = "${pkgs.fuzzel}/bin/fuzzel";
|
||||
sed = "${pkgs.gnused}/bin/sed";
|
||||
wtype = "${pkgs.wtype}/bin/wtype";
|
||||
kitty = "${pkgs.kitty}/bin/kitty";
|
||||
launcher-cmd = fuzzel;
|
||||
terminal-cmd = kitty;
|
||||
lock-cmd = "${pkgs.swaylock}/bin/swaylock --indicator-idle-visible --indicator-radius 100 --indicator-thickness 30";
|
||||
vol-up-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume +5";
|
||||
vol-down-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume -5";
|
||||
mute-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --toggle-mute";
|
||||
brightness-up-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set +2%";
|
||||
brightness-down-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set 2%-";
|
||||
screenshot-cmd = "${pkgs.sway-contrib.grimshot}/bin/grimshot copy area";
|
||||
# "bookmarking"/snippets inspired by Luke Smith:
|
||||
# - <https://www.youtube.com/watch?v=d_11QaTlf1I>
|
||||
snip-file = ../snippets.txt;
|
||||
# TODO: querying sops here breaks encapsulation
|
||||
list-snips = "cat ${snip-file} ${config.sops.secrets.snippets.path}";
|
||||
strip-comments = "${sed} 's/ #.*$//'";
|
||||
snip-cmd = "${wtype} $(${list-snips} | ${fuzzel} -d -i -w 60 | ${strip-comments})";
|
||||
# TODO: next splatmoji release should allow `-s none` to disable skin tones
|
||||
emoji-cmd = "${pkgs.splatmoji}/bin/splatmoji -s medium-light type";
|
||||
in ''
|
||||
### default font
|
||||
font pango:monospace 8
|
||||
|
||||
### pixel boundary between windows
|
||||
default_border pixel 3
|
||||
default_floating_border pixel 2
|
||||
hide_edge_borders smart
|
||||
|
||||
### defaults
|
||||
focus_wrapping no
|
||||
focus_follows_mouse yes
|
||||
focus_on_window_activation smart
|
||||
mouse_warping output
|
||||
workspace_layout default
|
||||
workspace_auto_back_and_forth no
|
||||
|
||||
### default colors (#border #background #text #indicator #childBorder)
|
||||
client.focused #4c7899 #285577 #ffffff #2e9ef4 #285577
|
||||
client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
|
||||
client.unfocused #333333 #222222 #888888 #292d2e #222222
|
||||
client.urgent #2f343a #900000 #ffffff #900000 #900000
|
||||
client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
|
||||
client.background #ffffff
|
||||
|
||||
### key bindings
|
||||
floating_modifier Mod1
|
||||
## media keys
|
||||
bindsym XF86AudioRaiseVolume exec ${vol-up-cmd}
|
||||
bindsym XF86AudioLowerVolume exec ${vol-down-cmd}
|
||||
bindsym Mod1+Page_Up exec ${vol-up-cmd}
|
||||
bindsym Mod1+Page_Down exec ${vol-down-cmd}
|
||||
bindsym XF86AudioMute exec ${mute-cmd}
|
||||
bindsym XF86MonBrightnessUp exec ${brightness-up-cmd}
|
||||
bindsym XF86MonBrightnessDown exec ${brightness-down-cmd}
|
||||
## special functions
|
||||
bindsym Mod1+Print exec ${screenshot-cmd}
|
||||
bindsym Mod1+l exec ${lock-cmd}
|
||||
bindsym Mod1+s exec ${snip-cmd}
|
||||
bindsym Mod1+slash exec ${emoji-cmd}
|
||||
bindsym Mod1+d exec ${launcher-cmd}
|
||||
bindsym Mod1+Return exec ${terminal-cmd}
|
||||
bindsym Mod1+Shift+q kill
|
||||
bindsym Mod1+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'
|
||||
bindsym Mod1+Shift+c reload
|
||||
## layout
|
||||
bindsym Mod1+b splith
|
||||
bindsym Mod1+v splitv
|
||||
bindsym Mod1+f fullscreen toggle
|
||||
bindsym Mod1+a focus parent
|
||||
bindsym Mod1+w layout tabbed
|
||||
bindsym Mod1+e layout toggle split
|
||||
bindsym Mod1+Shift+space floating toggle
|
||||
bindsym Mod1+space focus mode_toggle
|
||||
bindsym Mod1+r mode resize
|
||||
## movement
|
||||
bindsym Mod1+Up focus up
|
||||
bindsym Mod1+Down focus down
|
||||
bindsym Mod1+Left focus left
|
||||
bindsym Mod1+Right focus right
|
||||
bindsym Mod1+Shift+Up move up
|
||||
bindsym Mod1+Shift+Down move down
|
||||
bindsym Mod1+Shift+Left move left
|
||||
bindsym Mod1+Shift+Right move right
|
||||
## workspaces
|
||||
bindsym Mod1+1 workspace number 1
|
||||
bindsym Mod1+2 workspace number 2
|
||||
bindsym Mod1+3 workspace number 3
|
||||
bindsym Mod1+4 workspace number 4
|
||||
bindsym Mod1+5 workspace number 5
|
||||
bindsym Mod1+6 workspace number 6
|
||||
bindsym Mod1+7 workspace number 7
|
||||
bindsym Mod1+8 workspace number 8
|
||||
bindsym Mod1+9 workspace number 9
|
||||
bindsym Mod1+Shift+1 move container to workspace number 1
|
||||
bindsym Mod1+Shift+2 move container to workspace number 2
|
||||
bindsym Mod1+Shift+3 move container to workspace number 3
|
||||
bindsym Mod1+Shift+4 move container to workspace number 4
|
||||
bindsym Mod1+Shift+5 move container to workspace number 5
|
||||
bindsym Mod1+Shift+6 move container to workspace number 6
|
||||
bindsym Mod1+Shift+7 move container to workspace number 7
|
||||
bindsym Mod1+Shift+8 move container to workspace number 8
|
||||
bindsym Mod1+Shift+9 move container to workspace number 9
|
||||
## "scratchpad" = ??
|
||||
bindsym Mod1+Shift+minus move scratchpad
|
||||
bindsym Mod1+minus scratchpad show
|
||||
|
||||
### defaults
|
||||
mode "resize" {
|
||||
bindsym Down resize grow height 10 px
|
||||
bindsym Escape mode default
|
||||
bindsym Left resize shrink width 10 px
|
||||
bindsym Return mode default
|
||||
bindsym Right resize grow width 10 px
|
||||
bindsym Up resize shrink height 10 px
|
||||
bindsym h resize shrink width 10 px
|
||||
bindsym j resize grow height 10 px
|
||||
bindsym k resize shrink height 10 px
|
||||
bindsym l resize grow width 10 px
|
||||
}
|
||||
|
||||
### lightly modified bars
|
||||
bar {
|
||||
# TODO: fonts was:
|
||||
# config.fonts.fontconfig.defaultFonts; (monospace ++ emoji)
|
||||
font pango:Hack, Font Awesome 6 Free, Twitter Color Emoji 24.000000
|
||||
mode dock
|
||||
hidden_state hide
|
||||
position top
|
||||
status_command ${pkgs.i3status}/bin/i3status
|
||||
swaybar_command ${pkgs.waybar}/bin/waybar
|
||||
workspace_buttons yes
|
||||
strip_workspace_numbers no
|
||||
tray_output primary
|
||||
colors {
|
||||
background #000000
|
||||
statusline #ffffff
|
||||
separator #666666
|
||||
# #border #background #text
|
||||
focused_workspace #4c7899 #285577 #ffffff
|
||||
active_workspace #333333 #5f676a #ffffff
|
||||
inactive_workspace #333333 #222222 #888888
|
||||
urgent_workspace #2f343a #900000 #ffffff
|
||||
binding_mode #2f343a #900000 #ffffff
|
||||
}
|
||||
}
|
||||
|
||||
### displays
|
||||
## DESKTOP
|
||||
output "Samsung Electric Company S22C300 0x00007F35" {
|
||||
pos 0,0
|
||||
res 1920x1080
|
||||
}
|
||||
output "Goldstar Company Ltd LG ULTRAWIDE 0x00004E94" {
|
||||
pos 1920,0
|
||||
res 3440x1440
|
||||
}
|
||||
|
||||
## LAPTOP
|
||||
# sh/en TV
|
||||
output "Pioneer Electronic Corporation VSX-524 0x00000101" {
|
||||
pos 0,0
|
||||
res 1920x1080
|
||||
}
|
||||
# internal display
|
||||
output "Unknown 0x0637 0x00000000" {
|
||||
pos 1920,0
|
||||
res 1920x1080
|
||||
}
|
||||
''
|
67
hosts/modules/gui/sway/waybar-config.nix
Normal file
67
hosts/modules/gui/sway/waybar-config.nix
Normal file
@@ -0,0 +1,67 @@
|
||||
# docs: https://github.com/Alexays/Waybar/wiki/Configuration
|
||||
# format specifiers: https://fmt.dev/latest/syntax.html#syntax
|
||||
{ pkgs }:
|
||||
[
|
||||
{ # TOP BAR
|
||||
layer = "top";
|
||||
height = 40;
|
||||
modules-left = ["sway/workspaces" "sway/mode"];
|
||||
modules-center = ["sway/window"];
|
||||
modules-right = ["custom/mediaplayer" "clock" "battery" "cpu" "network"];
|
||||
"sway/window" = {
|
||||
max-length = 50;
|
||||
};
|
||||
# include song artist/title. source: https://www.reddit.com/r/swaywm/comments/ni0vso/waybar_spotify_tracktitle/
|
||||
"custom/mediaplayer" = {
|
||||
exec = pkgs.writeShellScript "waybar-mediaplayer" ''
|
||||
player_status=$(${pkgs.playerctl}/bin/playerctl status 2> /dev/null)
|
||||
if [ "$player_status" = "Playing" ]; then
|
||||
echo "$(${pkgs.playerctl}/bin/playerctl metadata artist) - $(${pkgs.playerctl}/bin/playerctl metadata title)"
|
||||
elif [ "$player_status" = "Paused" ]; then
|
||||
echo " $(${pkgs.playerctl}/bin/playerctl metadata artist) - $(${pkgs.playerctl}/bin/playerctl metadata title)"
|
||||
fi
|
||||
'';
|
||||
interval = 2;
|
||||
format = "{} ";
|
||||
# return-type = "json";
|
||||
on-click = "${pkgs.playerctl}/bin/playerctl play-pause";
|
||||
on-scroll-up = "${pkgs.playerctl}/bin/playerctl next";
|
||||
on-scroll-down = "${pkgs.playerctl}/bin/playerctl previous";
|
||||
};
|
||||
network = {
|
||||
# docs: https://github.com/Alexays/Waybar/blob/master/man/waybar-network.5.scd
|
||||
interval = 2;
|
||||
max-length = 40;
|
||||
# custom :> format specifier explained here: https://github.com/Alexays/Waybar/pull/472
|
||||
format-ethernet = " {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
||||
tooltip-format-ethernet = "{ifname} {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
||||
|
||||
format-wifi = "{ifname} ({signalStrength}%) {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
||||
tooltip-format-wifi = "{essid} ({signalStrength}%) {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
||||
|
||||
format-disconnected = "";
|
||||
};
|
||||
cpu = {
|
||||
format = " {usage:2}%";
|
||||
tooltip = false;
|
||||
};
|
||||
battery = {
|
||||
states = {
|
||||
good = 95;
|
||||
warning = 30;
|
||||
critical = 10;
|
||||
};
|
||||
format = "{icon} {capacity}%";
|
||||
format-icons = [
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
];
|
||||
};
|
||||
clock = {
|
||||
format-alt = "{:%a, %d. %b %H:%M}";
|
||||
};
|
||||
}
|
||||
]
|
256
hosts/modules/gui/sway/waybar-style.css
Normal file
256
hosts/modules/gui/sway/waybar-style.css
Normal file
@@ -0,0 +1,256 @@
|
||||
/* style docs: https://github.com/Alexays/Waybar/wiki/Styling */
|
||||
|
||||
* {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
/* defaults below: https://github.com/Alexays/Waybar/blob/master/resources/style.css */
|
||||
window#waybar {
|
||||
background-color: rgba(43, 48, 59, 0.5);
|
||||
border-bottom: 3px solid rgba(100, 114, 125, 0.5);
|
||||
color: #ffffff;
|
||||
transition-property: background-color;
|
||||
transition-duration: .5s;
|
||||
}
|
||||
|
||||
window#waybar.hidden {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
/*
|
||||
window#waybar.empty {
|
||||
background-color: transparent;
|
||||
}
|
||||
window#waybar.solo {
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
*/
|
||||
|
||||
window#waybar.termite {
|
||||
background-color: #3F3F3F;
|
||||
}
|
||||
|
||||
window#waybar.chromium {
|
||||
background-color: #000000;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#workspaces button {
|
||||
padding: 0 5px;
|
||||
background-color: transparent;
|
||||
color: #ffffff;
|
||||
/* Use box-shadow instead of border so the text isn't offset */
|
||||
box-shadow: inset 0 -3px transparent;
|
||||
/* Avoid rounded borders under each workspace name */
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
|
||||
#workspaces button:hover {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
box-shadow: inset 0 -3px #ffffff;
|
||||
}
|
||||
|
||||
#workspaces button.focused {
|
||||
background-color: #64727D;
|
||||
box-shadow: inset 0 -3px #ffffff;
|
||||
}
|
||||
|
||||
#workspaces button.urgent {
|
||||
background-color: #eb4d4b;
|
||||
}
|
||||
|
||||
#mode {
|
||||
background-color: #64727D;
|
||||
border-bottom: 3px solid #ffffff;
|
||||
}
|
||||
|
||||
#clock,
|
||||
#battery,
|
||||
#cpu,
|
||||
#memory,
|
||||
#disk,
|
||||
#temperature,
|
||||
#backlight,
|
||||
#network,
|
||||
#pulseaudio,
|
||||
#custom-media,
|
||||
#tray,
|
||||
#mode,
|
||||
#idle_inhibitor,
|
||||
#mpd {
|
||||
padding: 0 10px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#window,
|
||||
#workspaces {
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
/* If workspaces is the leftmost module, omit left margin */
|
||||
.modules-left > widget:first-child > #workspaces {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
/* If workspaces is the rightmost module, omit right margin */
|
||||
.modules-right > widget:last-child > #workspaces {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
#clock {
|
||||
background-color: #64727D;
|
||||
}
|
||||
|
||||
#battery {
|
||||
background-color: #ffffff;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
#battery.charging, #battery.plugged {
|
||||
color: #ffffff;
|
||||
background-color: #26A65B;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
to {
|
||||
background-color: #ffffff;
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
|
||||
#battery.critical:not(.charging) {
|
||||
background-color: #f53c3c;
|
||||
color: #ffffff;
|
||||
animation-name: blink;
|
||||
animation-duration: 0.5s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
}
|
||||
|
||||
label:focus {
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
#cpu {
|
||||
background-color: #2ecc71;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
#memory {
|
||||
background-color: #9b59b6;
|
||||
}
|
||||
|
||||
#disk {
|
||||
background-color: #964B00;
|
||||
}
|
||||
|
||||
#backlight {
|
||||
background-color: #90b1b1;
|
||||
}
|
||||
|
||||
#network {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
#network.disconnected {
|
||||
background-color: #f53c3c;
|
||||
}
|
||||
|
||||
#pulseaudio {
|
||||
background-color: #f1c40f;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
#pulseaudio.muted {
|
||||
background-color: #90b1b1;
|
||||
color: #2a5c45;
|
||||
}
|
||||
|
||||
#custom-media {
|
||||
background-color: #66cc99;
|
||||
color: #2a5c45;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
#custom-media.custom-spotify {
|
||||
background-color: #66cc99;
|
||||
}
|
||||
|
||||
#custom-media.custom-vlc {
|
||||
background-color: #ffa000;
|
||||
}
|
||||
|
||||
#temperature {
|
||||
background-color: #f0932b;
|
||||
}
|
||||
|
||||
#temperature.critical {
|
||||
background-color: #eb4d4b;
|
||||
}
|
||||
|
||||
#tray {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
#tray > .passive {
|
||||
-gtk-icon-effect: dim;
|
||||
}
|
||||
|
||||
#tray > .needs-attention {
|
||||
-gtk-icon-effect: highlight;
|
||||
background-color: #eb4d4b;
|
||||
}
|
||||
|
||||
#idle_inhibitor {
|
||||
background-color: #2d3436;
|
||||
}
|
||||
|
||||
#idle_inhibitor.activated {
|
||||
background-color: #ecf0f1;
|
||||
color: #2d3436;
|
||||
}
|
||||
|
||||
#mpd {
|
||||
background-color: #66cc99;
|
||||
color: #2a5c45;
|
||||
}
|
||||
|
||||
#mpd.disconnected {
|
||||
background-color: #f53c3c;
|
||||
}
|
||||
|
||||
#mpd.stopped {
|
||||
background-color: #90b1b1;
|
||||
}
|
||||
|
||||
#mpd.paused {
|
||||
background-color: #51a37a;
|
||||
}
|
||||
|
||||
#language {
|
||||
background: #00b093;
|
||||
color: #740864;
|
||||
padding: 0 5px;
|
||||
margin: 0 5px;
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
#keyboard-state {
|
||||
background: #97e1ad;
|
||||
color: #000000;
|
||||
padding: 0 0px;
|
||||
margin: 0 5px;
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
#keyboard-state > label {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
#keyboard-state > label.locked {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
280
hosts/modules/gui/sxmo.nix
Normal file
280
hosts/modules/gui/sxmo.nix
Normal file
@@ -0,0 +1,280 @@
|
||||
# this work derives from noneucat's sxmo service/packages, found via NUR
|
||||
# - <repo:nix-community/nur-combined:repos/noneucat/modules/pinephone/sxmo.nix>
|
||||
# other nix works:
|
||||
# - <https://github.com/wentam/sxmo-nix>
|
||||
# - implements sxmo atop tinydm (also packaged by wentam)
|
||||
# - wentam cleans up sxmo-utils to be sealed. also patches to use systemd poweroff, etc
|
||||
# - packages a handful of anjan and proycon utilities
|
||||
# - packages <https://gitlab.com/kop316/mmsd/>
|
||||
# - packages <https://gitlab.com/kop316/vvmd/>
|
||||
# - <https://github.com/chuangzhu/nixpkgs-sxmo>
|
||||
# - implements sxmo as a direct systemd service -- apparently no DM
|
||||
# - packages sxmo-utils
|
||||
# - injects PATH into each script
|
||||
# - perhaps sxmo-utils is best packaged via the `resholve` shell solver?
|
||||
#
|
||||
# sxmo documentation:
|
||||
# - <repo:anjan/sxmo-docs-next>
|
||||
#
|
||||
# sxmo technical overview:
|
||||
# - inputs
|
||||
# - dwm: handles vol/power buttons; hardcoded in config.h
|
||||
# - lisgd: handles gestures
|
||||
# - startup
|
||||
# - daemon based (lisgsd, idle_locker, statusbar_periodics)
|
||||
# - auto-started at login
|
||||
# - managable by `sxmo_daemons.sh`
|
||||
# - list available daemons: `sxmo_daemons.sh list`
|
||||
# - query if a daemon is active: `sxmo_daemons.sh running <my-daemon>`
|
||||
# - start daemon: `sxmo_daemons.sh start <my-daemon>`
|
||||
# - managable by `superctl`
|
||||
# - `superctl status`
|
||||
# - user hooks:
|
||||
# - live in ~/.config/sxmo/hooks/
|
||||
# - logs:
|
||||
# - live in ~/.local/state/sxmo.log
|
||||
# - ~/.local/state/superd.log
|
||||
# - ~/.local/state/superd/logs/<daemon>.log
|
||||
# - `journalctl --user --boot` (lightm redirects the sxmo session stdout => systemd)
|
||||
#
|
||||
# - default components:
|
||||
# - DE: sway (if wayland), dwm (if X)
|
||||
# - menus: bemenu (if wayland), dmenu (if X)
|
||||
# - gestures: lisgd
|
||||
# - on-screen keyboard: wvkbd (if wayland), svkbd (if X)
|
||||
#
|
||||
{ lib, config, pkgs, sane-lib, ... }:
|
||||
|
||||
let
|
||||
cfg = config.sane.gui.sxmo;
|
||||
in
|
||||
{
|
||||
options = with lib; {
|
||||
sane.gui.sxmo.enable = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
};
|
||||
sane.gui.sxmo.greeter = mkOption {
|
||||
type = types.enum [ "lightdm-mobile" "sway" ];
|
||||
default = "lightdm-mobile";
|
||||
description = ''
|
||||
which greeter to use.
|
||||
"lightdm-mobile" => keypad style greeter. can only enter digits 0-9 as password.
|
||||
"sway" => layered sway greeter. behaves as if you booted to swaylock.
|
||||
'';
|
||||
};
|
||||
sane.gui.sxmo.hooks = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.runCommand "sxmo-hooks" { } ''
|
||||
mkdir -p $out
|
||||
ln -s ${pkgs.sxmo-utils}/share/sxmo/default_hooks $out/bin
|
||||
'';
|
||||
description = ''
|
||||
hooks to make visible to sxmo.
|
||||
a hook is a script generally of the name sxmo_hook_<thing>.sh
|
||||
which is called by sxmo at key moments to proide user programmability.
|
||||
'';
|
||||
};
|
||||
sane.gui.sxmo.deviceHooks = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.runCommand "sxmo-device-hooks" { } ''
|
||||
mkdir -p $out
|
||||
ln -s ${pkgs.sxmo-utils}/share/sxmo/default_hooks/unknown $out/bin
|
||||
'';
|
||||
description = ''
|
||||
device-specific hooks to make visible to sxmo.
|
||||
this package supplies things like `sxmo_hook_inputhandler.sh`.
|
||||
a hook is a script generally of the name sxmo_hook_<thing>.sh
|
||||
which is called by sxmo at key moments to proide user programmability.
|
||||
'';
|
||||
};
|
||||
sane.gui.sxmo.terminal = mkOption {
|
||||
# type = types.nullOr (types.enum [ "foot" "st" "vte" ]);
|
||||
type = types.nullOr types.str;
|
||||
default = "foot";
|
||||
description = ''
|
||||
name of terminal to use for sxmo_terminal.sh.
|
||||
foot, st, and vte have special integrations in sxmo, but any will work.
|
||||
'';
|
||||
};
|
||||
sane.gui.sxmo.keyboard = mkOption {
|
||||
# type = types.nullOr (types.enum ["wvkbd"])
|
||||
type = types.nullOr types.str;
|
||||
default = "wvkbd";
|
||||
description = ''
|
||||
name of on-screen-keyboard to use for sxmo_keyboard.sh.
|
||||
this sets the KEYBOARD environment variable.
|
||||
see also: KEYBOARD_ARGS.
|
||||
'';
|
||||
};
|
||||
sane.gui.sxmo.settings = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = {};
|
||||
description = ''
|
||||
environment variables used to configure sxmo.
|
||||
e.g. SXMO_UNLOCK_IDLE_TIME or SXMO_VOLUME_BUTTON.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
{
|
||||
sane.programs.sxmoApps = {
|
||||
package = null;
|
||||
suggestedPrograms = [
|
||||
"guiApps"
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
(lib.mkIf cfg.enable {
|
||||
sane.programs.sxmoApps.enableFor.user.colin = true;
|
||||
|
||||
# some programs (e.g. fractal/nheko) **require** a "Secret Service Provider"
|
||||
services.gnome.gnome-keyring.enable = true;
|
||||
|
||||
# TODO: probably need to enable pipewire
|
||||
|
||||
networking.useDHCP = false;
|
||||
networking.networkmanager.enable = true;
|
||||
networking.wireless.enable = lib.mkForce false;
|
||||
|
||||
hardware.bluetooth.enable = true;
|
||||
services.blueman.enable = true;
|
||||
|
||||
# sxmo internally uses doas instead of sudo
|
||||
security.doas.enable = true;
|
||||
security.doas.wheelNeedsPassword = false;
|
||||
|
||||
# TODO: not all of these fonts seem to be mapped to the correct icon
|
||||
fonts.fonts = [ pkgs.nerdfonts ];
|
||||
|
||||
# i believe sxmo recomments a different audio stack
|
||||
# administer with pw-cli, pw-mon, pw-top commands
|
||||
services.pipewire = {
|
||||
enable = true;
|
||||
alsa.enable = true;
|
||||
alsa.support32Bit = true; # ??
|
||||
pulse.enable = true;
|
||||
};
|
||||
systemd.user.services."pipewire".wantedBy = [ "graphical-session.target" ];
|
||||
|
||||
# TODO: could use `displayManager.sessionPackages`?
|
||||
environment.systemPackages = with pkgs; [
|
||||
bc
|
||||
bemenu
|
||||
bonsai
|
||||
conky
|
||||
gojq
|
||||
inotify-tools
|
||||
jq
|
||||
libnotify
|
||||
lisgd
|
||||
mako
|
||||
superd
|
||||
sway
|
||||
swayidle
|
||||
sxmo-utils
|
||||
wob
|
||||
wvkbd
|
||||
xdg-user-dirs
|
||||
|
||||
# X11 only?
|
||||
xdotool
|
||||
|
||||
cfg.deviceHooks
|
||||
cfg.hooks
|
||||
] ++ lib.optionals (cfg.terminal != null) [ pkgs."${cfg.terminal}" ]
|
||||
++ lib.optionals (cfg.keyboard != null) [ pkgs."${cfg.keyboard}" ];
|
||||
|
||||
environment.sessionVariables = {
|
||||
XDG_DATA_DIRS = [
|
||||
# TODO: only need the share/sxmo directly linked
|
||||
"${pkgs.sxmo-utils}/share"
|
||||
];
|
||||
} // lib.optionalAttrs (cfg.terminal != null) {
|
||||
TERMCMD = lib.mkDefault (if cfg.terminal == "vte" then "vte-2.91" else cfg.terminal);
|
||||
} // lib.optionalAttrs (cfg.keyboard != null) {
|
||||
KEYBOARD = lib.mkDefault (if cfg.keyboard == "wvkbd" then "wvkbd-mobintl" else cfg.keyboard);
|
||||
} // cfg.settings;
|
||||
|
||||
sane.user.fs.".cache/sxmo/sxmo.noidle" = sane-lib.fs.wantedText "";
|
||||
|
||||
|
||||
## greeter
|
||||
|
||||
services.xserver = lib.mkIf (cfg.greeter == "lightdm-mobile") {
|
||||
enable = true;
|
||||
|
||||
displayManager.lightdm.enable = true;
|
||||
displayManager.lightdm.greeters.mobile.enable = true;
|
||||
displayManager.lightdm.extraSeatDefaults = ''
|
||||
user-session = swmo
|
||||
'';
|
||||
|
||||
displayManager.sessionPackages = with pkgs; [
|
||||
sxmo-utils # this gets share/wayland-sessions/swmo.desktop linked
|
||||
];
|
||||
|
||||
# taken from gui/phosh:
|
||||
# NB: setting defaultSession has the critical side-effect that it lets org.freedesktop.AccountsService
|
||||
# know that our user exists. this ensures lightdm succeeds when calling /org/freedesktop/AccountsServices ListCachedUsers
|
||||
# lightdm greeters get the login users from lightdm which gets it from org.freedesktop.Accounts.ListCachedUsers.
|
||||
# this requires the user we want to login as to be cached.
|
||||
displayManager.job.preStart = ''
|
||||
${pkgs.systemd}/bin/busctl call org.freedesktop.Accounts /org/freedesktop/Accounts org.freedesktop.Accounts CacheUser s colin
|
||||
'';
|
||||
};
|
||||
|
||||
services.greetd = lib.mkIf (cfg.greeter == "sway") {
|
||||
enable = true;
|
||||
# borrowed from gui/sway
|
||||
settings.default_session.command =
|
||||
let
|
||||
# start sway and have it construct the gtkgreeter
|
||||
sway-as-greeter = pkgs.writeShellScriptBin "sway-as-greeter" ''
|
||||
${pkgs.sway}/bin/sway --debug --config ${sway-config-into-gtkgreet} > /var/log/sway/sway-as-greeter.log 2>&1
|
||||
'';
|
||||
# (config file for the above)
|
||||
sway-config-into-gtkgreet = pkgs.writeText "greetd-sway-config" ''
|
||||
exec "${gtkgreet-launcher}"
|
||||
'';
|
||||
# gtkgreet which launches a layered sway instance
|
||||
gtkgreet-launcher = pkgs.writeShellScript "gtkgreet-launcher" ''
|
||||
# NB: the "command" field here is run in the user's shell.
|
||||
# so that command must exist on the specific user's path who is logging in. it doesn't need to exist system-wide.
|
||||
${pkgs.greetd.gtkgreet}/bin/gtkgreet --layer-shell --command sxmo_winit.sh
|
||||
'';
|
||||
in "${sway-as-greeter}/bin/sway-as-greeter";
|
||||
};
|
||||
|
||||
sane.fs."/var/log/sway" = lib.mkIf (cfg.greeter == "sway") {
|
||||
dir.acl.mode = "0777";
|
||||
wantedBeforeBy = [ "greetd.service" "display-manager.service" ];
|
||||
};
|
||||
|
||||
# lightdm-mobile-greeter: "The name org.a11y.Bus was not provided by any .service files"
|
||||
services.gnome.at-spi2-core.enable = true;
|
||||
|
||||
# services.xserver.windowManager.session = [{
|
||||
# name = "sxmo";
|
||||
# desktopNames = [ "sxmo" ];
|
||||
# start = ''
|
||||
# ${pkgs.sxmo-utils}/bin/sxmo_xinit.sh &
|
||||
# waitPID=$!
|
||||
# '';
|
||||
# }];
|
||||
# services.xserver.enable = true;
|
||||
|
||||
# services.greetd = {
|
||||
# enable = true;
|
||||
# settings = {
|
||||
# default_session = {
|
||||
# command = "${pkgs.sxmo-utils}/bin/sxmo_winit.sh";
|
||||
# user = "colin";
|
||||
# };
|
||||
# };
|
||||
# };
|
||||
})
|
||||
];
|
||||
}
|
@@ -69,7 +69,7 @@ in
|
||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFw9NoRaYrM6LbDd3aFBc4yyBlxGQn8HjeHd/dZ3CfHk";
|
||||
wg-home.pubkey = "17PMZssYi0D4t2d0vbmhjBKe1sGsE8kT8/dod0Q2CXc=";
|
||||
wg-home.ip = "10.0.10.22";
|
||||
lan-ip = "192.168.15.25";
|
||||
lan-ip = "10.78.79.52";
|
||||
};
|
||||
|
||||
sane.hosts.by-name."lappy" = {
|
||||
@@ -77,7 +77,7 @@ in
|
||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSJnqmVl9/SYQ0btvGb0REwwWY8wkdkGXQZfn/1geEc";
|
||||
wg-home.pubkey = "FTUWGw2p4/cEcrrIE86PWVnqctbv8OYpw8Gt3+dC/lk=";
|
||||
wg-home.ip = "10.0.10.20";
|
||||
lan-ip = "192.168.15.13";
|
||||
lan-ip = "10.78.79.53";
|
||||
};
|
||||
|
||||
sane.hosts.by-name."moby" = {
|
||||
@@ -85,7 +85,7 @@ in
|
||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO1N/IT3nQYUD+dBlU1sTEEVMxfOyMkrrDeyHcYgnJvw";
|
||||
wg-home.pubkey = "I7XIR1hm8bIzAtcAvbhWOwIAabGkuEvbWH/3kyIB1yA=";
|
||||
wg-home.ip = "10.0.10.48";
|
||||
lan-ip = "192.168.15.28";
|
||||
lan-ip = "10.78.79.54";
|
||||
};
|
||||
|
||||
sane.hosts.by-name."servo" = {
|
||||
@@ -94,7 +94,7 @@ in
|
||||
wg-home.pubkey = "roAw+IUFVtdpCcqa4khB385Qcv9l5JAB//730tyK4Wk=";
|
||||
wg-home.ip = "10.0.10.5";
|
||||
wg-home.endpoint = "uninsane.org:51820";
|
||||
lan-ip = "192.168.15.24";
|
||||
lan-ip = "10.78.79.51";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@@ -26,7 +26,7 @@ in
|
||||
type = types.bool;
|
||||
};
|
||||
sane.nixcache.substituters = mkOption {
|
||||
type = types.listOf types.string;
|
||||
type = types.listOf types.str;
|
||||
default =
|
||||
# TODO: make these blacklisted entries injectable
|
||||
(lib.optional (hostName != "servo") "https://nixcache.uninsane.org")
|
||||
|
16
hosts/modules/roles/ac.nix
Normal file
16
hosts/modules/roles/ac.nix
Normal file
@@ -0,0 +1,16 @@
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
options.sane.roles.ac = with lib; mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
services which you probably only want to use with AC power.
|
||||
specifically because they drain resources like power or bandwidth.
|
||||
'';
|
||||
};
|
||||
|
||||
config = lib.mkIf config.sane.roles.ac {
|
||||
# sane.yggdrasil.enable = true;
|
||||
# services.i2p.enable = true;
|
||||
};
|
||||
}
|
@@ -1,15 +1,23 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
install-iwd = pkgs.static-nix-shell.mkBash {
|
||||
pname = "install-iwd";
|
||||
src = ../../../../scripts;
|
||||
pkgs = [ "gnused" ];
|
||||
};
|
||||
in
|
||||
{
|
||||
config = lib.mkIf config.sane.roles.client {
|
||||
sane.fs."/var/lib/iwd/.secrets.psk.stamp" = {
|
||||
wantedBeforeBy = [ "iwd.service" ];
|
||||
generated.acl.mode = "0600";
|
||||
# XXX: install-iwd uses sed, but that's part of the default systemd unit path, it seems
|
||||
generated.script.script = builtins.readFile ../../../../scripts/install-iwd + ''
|
||||
generated.script.script = ''
|
||||
${install-iwd}/bin/install-iwd $@
|
||||
touch "/var/lib/iwd/.secrets.psk.stamp"
|
||||
'';
|
||||
generated.script.scriptArgs = [ "/run/secrets/iwd" "/var/lib/iwd" ];
|
||||
generated.script.scriptArgs = [ "/run/secrets/net" "/var/lib/iwd" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./ac.nix
|
||||
./build-machine.nix
|
||||
./client
|
||||
./dev-machine.nix
|
||||
];
|
||||
}
|
||||
|
30
hosts/modules/roles/dev-machine.nix
Normal file
30
hosts/modules/roles/dev-machine.nix
Normal file
@@ -0,0 +1,30 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
inherit (lib) mkIf mkMerge mkOption types;
|
||||
cfg = config.sane.roles.dev-machine;
|
||||
in
|
||||
{
|
||||
options.sane.roles.dev-machine = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
enable if this machine is used generally for "development"
|
||||
and you want tools to support that (e.g. docs).
|
||||
'';
|
||||
};
|
||||
|
||||
config = mkMerge [
|
||||
({
|
||||
sane.programs.docsets.config.rustPkgs = [
|
||||
"lemmy-server"
|
||||
"mx-sanebot"
|
||||
];
|
||||
})
|
||||
(mkIf cfg {
|
||||
sane.programs.docsets.enableFor.system = true;
|
||||
# TODO: migrate this to `sane.user.programs.zeal.enable = true`
|
||||
sane.programs.zeal.enableFor.user.colin = true;
|
||||
})
|
||||
];
|
||||
}
|
@@ -29,7 +29,7 @@ in
|
||||
# web-created keys are allowed to delete files, which you probably don't want for an incremental backup program
|
||||
# you need to create a new application key from the web in order to first get a key which can create new keys (use env vars in the above command)
|
||||
# TODO: s/duplicity_passphrase/duplicity_env/
|
||||
services.duplicity.secretFile = config.sops.secrets.duplicity_passphrase.path;
|
||||
services.duplicity.secretFile = config.sops.secrets."duplicity_passphrase.env".path;
|
||||
# NB: manually trigger with `systemctl start duplicity`
|
||||
services.duplicity.frequency = "daily";
|
||||
|
||||
|
@@ -33,6 +33,11 @@ in
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
sane.services.wg-home.enableWan = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "whether to make this port visible on the WAN";
|
||||
};
|
||||
sane.services.wg-home.ip = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
@@ -50,7 +55,12 @@ in
|
||||
# this config defines both the endpoint (server) and client configs
|
||||
|
||||
# for convenience, have both the server and client use the same port for their wireguard connections.
|
||||
networking.firewall.allowedUDPPorts = [ 51820 ];
|
||||
sane.ports.ports."51820" = {
|
||||
protocol = [ "udp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = cfg.enableWan;
|
||||
description = "colin-wireguard";
|
||||
};
|
||||
networking.wireguard.interfaces.wg-home = {
|
||||
listenPort = 51820;
|
||||
privateKeyFile = "/run/wg-home.priv";
|
||||
|
@@ -18,7 +18,7 @@ in
|
||||
services.yggdrasil = {
|
||||
enable = true;
|
||||
persistentKeys = true;
|
||||
config = {
|
||||
settings = {
|
||||
IFName = "ygg0";
|
||||
Peers = [
|
||||
"tls://longseason.1200bps.xyz:13122"
|
||||
|
@@ -22,6 +22,9 @@
|
||||
# ^ source: <https://github.com/nix-community/nur-packages-template/blob/master/.github/workflows/build.yml#L63>
|
||||
# N.B.: nur eval allows only PATH (inherited) and NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM="1" (forced),
|
||||
# hence the erasing of NIX_PATH above (to remove external overlays)
|
||||
#
|
||||
# if it validates here but not upstream, likely to do with different `nixpkgs` inputs.
|
||||
# - CI logs: <https://github.com/nix-community/NUR/actions/workflows/update.yml>
|
||||
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
let
|
||||
|
@@ -1,8 +1,9 @@
|
||||
{ lib, ... }:
|
||||
{ lib, sane-lib, ... }:
|
||||
|
||||
let
|
||||
inherit (builtins) concatLists concatStringsSep foldl' fromJSON map readDir readFile;
|
||||
inherit (lib) hasSuffix listToAttrs mapAttrsToList removeSuffix splitString;
|
||||
inherit (sane-lib) enumerateFilePaths;
|
||||
|
||||
# given a path to a .json file relative to sources, construct the best feed object we can.
|
||||
# the .json file could be empty, in which case we make assumptions about the feed based
|
||||
@@ -32,20 +33,5 @@ let
|
||||
fromJSON as-str;
|
||||
|
||||
sources = enumerateFilePaths ./sources;
|
||||
|
||||
# like `lib.listFilesRecursive` but does not mangle paths.
|
||||
# Type: enumerateFilePaths :: path -> [String]
|
||||
enumerateFilePaths = base:
|
||||
concatLists (
|
||||
mapAttrsToList
|
||||
(name: type:
|
||||
if type == "directory" then
|
||||
# enumerate this directory and then prefix each result with the directory's name
|
||||
map (e: "${name}/${e}") (enumerateFilePaths (base + "/${name}"))
|
||||
else
|
||||
[ name ]
|
||||
)
|
||||
(readDir base)
|
||||
);
|
||||
in
|
||||
listToAttrs (map feedFromSourcePath sources)
|
||||
|
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 1369733,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "Every company has a story. Learn the playbooks that built the world’s greatest companies — and how you can apply them as a founder, operator, or investor.",
|
||||
"favicon": null,
|
||||
"hubs": [],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 173,
|
||||
"last_seen": "2023-01-11T15:26:37.515527+00:00",
|
||||
"last_updated": "2022-12-19T07:22:28+00:00",
|
||||
"score": 18,
|
||||
"self_url": "https://acquired.libsyn.com/rss",
|
||||
"site_name": null,
|
||||
"site_url": null,
|
||||
"title": "Acquired",
|
||||
"url": "https://acquired.libsyn.com/rss",
|
||||
"velocity": 0.066,
|
||||
"version": "rss20"
|
||||
}
|
@@ -1,21 +1,23 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 443732,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "Ben and David are joined by expert founders and investors \u2014 writing the next generation of great company stories in real-time.\n\nWe go behind the scenes on their journeys and bring back emerging insights and lessons that are useful for anyone in the tech and investing ecosystems.\n\nAcquired covers yesterday. ACQ2 covers tomorrow.",
|
||||
"content_length": 567579,
|
||||
"content_type": "text/xml; charset=utf-8",
|
||||
"description": "ACQ2 is Ben and David's conversations with expert founders and investors.",
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [],
|
||||
"hubs": [
|
||||
"https://pubsubhubbub.appspot.com/"
|
||||
],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 92,
|
||||
"last_updated": "2023-03-02T17:03:15+00:00",
|
||||
"score": 10,
|
||||
"self_url": "https://acquiredlpbonussecretsecret.libsyn.com/",
|
||||
"site_name": "ACQ2 by Acquired",
|
||||
"site_url": "https://acquiredlpbonussecretsecret.libsyn.com",
|
||||
"title": "ACQ2 by Acquired",
|
||||
"url": "https://acquiredlpbonussecretsecret.libsyn.com",
|
||||
"velocity": 0.057,
|
||||
"is_push": true,
|
||||
"item_count": 91,
|
||||
"last_updated": "2023-05-09T06:51:48+00:00",
|
||||
"score": 24,
|
||||
"self_url": "https://feeds.transistor.fm/acq2",
|
||||
"site_name": "ACQ2: The Acquired Interviews",
|
||||
"site_url": "https://feeds.transistor.fm",
|
||||
"title": "ACQ2: The Acquired Interviews",
|
||||
"url": "https://feeds.transistor.fm/acq2",
|
||||
"velocity": 0.054,
|
||||
"version": "rss20"
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 1579416,
|
||||
"content_type": "text/xml; charset=utf-8",
|
||||
"description": "Every company has a story.",
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [
|
||||
"https://pubsubhubbub.appspot.com/"
|
||||
],
|
||||
"is_podcast": true,
|
||||
"is_push": true,
|
||||
"item_count": 178,
|
||||
"last_updated": "2023-05-30T05:02:40+00:00",
|
||||
"score": 24,
|
||||
"self_url": "https://feeds.transistor.fm/acquired",
|
||||
"site_name": "",
|
||||
"site_url": "https://feeds.transistor.fm",
|
||||
"title": "Acquired",
|
||||
"url": "https://feeds.transistor.fm/acquired",
|
||||
"velocity": 0.064,
|
||||
"version": "rss20"
|
||||
}
|
21
modules/data/feeds/sources/morningbrew.com/feed/default.json
Normal file
21
modules/data/feeds/sources/morningbrew.com/feed/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 151072,
|
||||
"content_type": "text/xml; charset=utf-8",
|
||||
"description": "The daily email newsletter covering the latest news from Wall St. to Silicon Valley. Informative, witty, and everything you need to start your day.",
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [],
|
||||
"is_podcast": false,
|
||||
"is_push": false,
|
||||
"item_count": 40,
|
||||
"last_updated": "2023-05-08T19:26:29+00:00",
|
||||
"score": 23,
|
||||
"self_url": "https://www.morningbrew.com/feed.xml",
|
||||
"site_name": "Morning Brew",
|
||||
"site_url": "https://www.morningbrew.com",
|
||||
"title": "Morning Brew",
|
||||
"url": "https://www.morningbrew.com/feed.xml",
|
||||
"velocity": 3.931,
|
||||
"version": "rss20"
|
||||
}
|
@@ -1,21 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 235911,
|
||||
"content_length": 145311,
|
||||
"content_type": "application/xml; charset=utf-8",
|
||||
"description": "<p>The Portal is an exploration into discovery, including conversations with thought leaders. Host Eric Weinstein, Managing Director of Thiel Capital, brings his unique expertise and diverse roster of guests for a wide range of discussions, including science, culture, business, and capitalism. The show will feature people whose lives demonstrate that portals into what we would normally consider impossible, are indeed possible. Guests include presidential candidate Andrew Yang, NY Times bestselling author Sam Harris, and retired Navy Seal and creator of the hit business podcast Jocko Willink.</p>",
|
||||
"favicon": null,
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 44,
|
||||
"last_seen": "2023-01-11T14:47:44.995855+00:00",
|
||||
"last_updated": "2020-12-02T07:50:55+00:00",
|
||||
"score": -12,
|
||||
"self_url": "https://www.omnycontent.com/d/playlist/9b7dacdf-a925-4f95-84dc-ac46003451ff/1713c520-edb6-43a3-b1b9-acb8002fdae7/58e33a0c-f86b-41c5-a11c-acb8002fdaf5/podcast.rss",
|
||||
"site_name": null,
|
||||
"site_url": null,
|
||||
"score": 8,
|
||||
"self_url": "",
|
||||
"site_name": "",
|
||||
"site_url": "",
|
||||
"title": "The Portal",
|
||||
"url": "https://www.omnycontent.com/d/playlist/9b7dacdf-a925-4f95-84dc-ac46003451ff/1713c520-edb6-43a3-b1b9-acb8002fdae7/58e33a0c-f86b-41c5-a11c-acb8002fdaf5/podcast.rss",
|
||||
"url": "https://feed.cdnstream1.com/zjb/feed/download/d9/8a/71/d98a71ac-d1a3-4d92-ab64-64b4ff3192d1.xml",
|
||||
"velocity": 0.082,
|
||||
"version": "rss20"
|
||||
}
|
||||
}
|
21
modules/data/feeds/sources/theregister.com/default.json
Normal file
21
modules/data/feeds/sources/theregister.com/default.json
Normal file
File diff suppressed because one or more lines are too long
21
modules/data/feeds/sources/tuxphones.com/default.json
Normal file
21
modules/data/feeds/sources/tuxphones.com/default.json
Normal file
File diff suppressed because one or more lines are too long
@@ -2,20 +2,22 @@
|
||||
|
||||
{
|
||||
imports = [
|
||||
./dns.nix
|
||||
./feeds.nix
|
||||
./fs
|
||||
./ids.nix
|
||||
./programs.nix
|
||||
./image.nix
|
||||
./persist
|
||||
./ports.nix
|
||||
./services
|
||||
./sops.nix
|
||||
./ssh.nix
|
||||
./users.nix
|
||||
];
|
||||
|
||||
_module.args = {
|
||||
_module.args = rec {
|
||||
sane-lib = import ./lib { inherit lib; };
|
||||
sane-data = import ./data { inherit lib; };
|
||||
sane-data = import ./data { inherit lib sane-lib; };
|
||||
};
|
||||
}
|
||||
|
145
modules/dns.nix
Normal file
145
modules/dns.nix
Normal file
@@ -0,0 +1,145 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.sane.dns;
|
||||
toml = pkgs.formats.toml { };
|
||||
recordFormatters = {
|
||||
# quote rules for zone files:
|
||||
# - any character may be encoded by `\DDD`, where `DDD` represents its ascii value in base 8.
|
||||
# - any non-digit `X` may be encoded by `\X`.
|
||||
# - stated in: <https://www.ietf.org/rfc/rfc1035.txt>: 5.1 Format
|
||||
# - visible in <trust-dns:crates/proto/src/serialize/txt/zone_lex.rs:escape_seq>
|
||||
# for us, we can just replace `\` => `\\ and `"` -> `\"`
|
||||
TXT = value: "\"" + (lib.escape [ "\\" "\"" ] value) + "\"";
|
||||
};
|
||||
# proto: "INET", etc
|
||||
# rrtype: "TXT", "A", "CNAME", etc
|
||||
fmtRecord = proto: rrtype: name: value:
|
||||
let
|
||||
formatter = recordFormatters."${rrtype}" or lib.id;
|
||||
in
|
||||
"${name}\t${proto}\t${rrtype}\t${formatter value}";
|
||||
fmtRecordList = proto: rrtype: name: values: concatStringsSep
|
||||
"\n"
|
||||
(map (fmtRecord proto rrtype name) values)
|
||||
;
|
||||
fmtRecordAttrs = proto: rrtype: rrAttrs:
|
||||
concatStringsSep
|
||||
"\n"
|
||||
(
|
||||
attrValues (
|
||||
mapAttrs
|
||||
(name: fmtRecordList proto rrtype name)
|
||||
rrAttrs
|
||||
)
|
||||
);
|
||||
# format other .zone files to include into this one
|
||||
fmtIncludes = paths: concatStringsSep
|
||||
"\n"
|
||||
(map (path: "$INCLUDE ${path}") paths);
|
||||
|
||||
genZone = zcfg: ''
|
||||
$TTL ${toString zcfg.TTL}
|
||||
${fmtRecordAttrs "IN" "SOA" zcfg.inet.SOA}
|
||||
${fmtRecordAttrs "IN" "A" zcfg.inet.A}
|
||||
${fmtRecordAttrs "IN" "CNAME" zcfg.inet.CNAME}
|
||||
${fmtRecordAttrs "IN" "MX" zcfg.inet.MX}
|
||||
${fmtRecordAttrs "IN" "NS" zcfg.inet.NS}
|
||||
${fmtRecordAttrs "IN" "SRV" zcfg.inet.SRV}
|
||||
${fmtRecordAttrs "IN" "TXT" zcfg.inet.TXT}
|
||||
${fmtIncludes zcfg.include}
|
||||
${zcfg.extraConfig}
|
||||
'';
|
||||
|
||||
# (listOf ty) type which also accepts single-assignment of `ty`.
|
||||
# it's used to allow the user to write:
|
||||
# CNAME."foo" = "bar";
|
||||
# as shorthand for
|
||||
# CNAME."foo" = [ "bar" ];
|
||||
listOrUnit = ty: types.coercedTo ty (elem: [ elem ]) (types.listOf ty);
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sane.dns = {
|
||||
zones = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = "zone name. defaults to the attribute name in zones";
|
||||
default = null;
|
||||
};
|
||||
TTL = mkOption {
|
||||
type = types.int;
|
||||
description = "default TTL";
|
||||
default = 3600;
|
||||
};
|
||||
include = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "paths of other zone files to $INCLUDE into this one";
|
||||
default = [];
|
||||
};
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
description = "extra lines to append to the zone file";
|
||||
default = "";
|
||||
};
|
||||
inet = {
|
||||
SOA = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "Start of Authority record(s)";
|
||||
default = {};
|
||||
};
|
||||
A = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "IPv4 address record(s)";
|
||||
default = {};
|
||||
};
|
||||
CNAME = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "canonical name record(s)";
|
||||
default = {};
|
||||
};
|
||||
MX = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "mail exchanger record(s)";
|
||||
default = {};
|
||||
};
|
||||
NS = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "name server record(s)";
|
||||
default = {};
|
||||
};
|
||||
SRV = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "service record(s)";
|
||||
default = {};
|
||||
};
|
||||
TXT = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "text record(s)";
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
|
||||
file = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
instead of using the generated zone file, use the specified path (user should populate the file specified here).
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
default = {};
|
||||
description = "Declarative zone config";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
sane.services.trust-dns.zones = builtins.mapAttrs (_name: zcfg: {
|
||||
text = genZone zcfg;
|
||||
}) cfg.zones;
|
||||
};
|
||||
}
|
@@ -341,6 +341,8 @@ in {
|
||||
options = {
|
||||
sane.fs = mkOption {
|
||||
# type = types.attrsOf fsEntry;
|
||||
# TODO: can we use `types.lazyAttrsOf fsEntry`??
|
||||
# - this exists specifically to let attrs reference eachother
|
||||
type = fsTree;
|
||||
default = {};
|
||||
};
|
||||
|
@@ -72,5 +72,22 @@ sane-lib = rec {
|
||||
inherit path value;
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
# like `lib.listFilesRecursive` but does not mangle paths.
|
||||
#
|
||||
# Type: enumerateFilePaths :: path -> [String]
|
||||
enumerateFilePaths = base:
|
||||
builtins.concatLists (
|
||||
lib.mapAttrsToList
|
||||
(name: type:
|
||||
if type == "directory" then
|
||||
# enumerate this directory and then prefix each result with the directory's name
|
||||
builtins.map (e: "${name}/${e}") (enumerateFilePaths (base + "/${name}"))
|
||||
else
|
||||
[ name ]
|
||||
)
|
||||
(builtins.readDir base)
|
||||
);
|
||||
};
|
||||
in sane-lib
|
||||
|
113
modules/ports.nix
Normal file
113
modules/ports.nix
Normal file
@@ -0,0 +1,113 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sane.ports;
|
||||
|
||||
portOpts = with lib; types.submodule {
|
||||
options = {
|
||||
protocol = mkOption {
|
||||
type = types.listOf (types.enum [ "udp" "tcp" ]);
|
||||
};
|
||||
visibleTo.lan = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
# XXX: if a service is visible to the WAN, it ends up visible to the LAN as well.
|
||||
# technically solvable (explicitly drop packets delivered from LAN IPs) but doesn't make much sense.
|
||||
};
|
||||
visibleTo.wan = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
visibleTo.ovpn = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
# XXX: behaves more or less the same as `lan` visibility.
|
||||
# OVPN passes everything by default.
|
||||
# TODO: have *this* drive what we forward from wireguard namespace to main namespace
|
||||
};
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
default = "colin-${config.net.hostName}";
|
||||
description = ''
|
||||
short description of why this port is open.
|
||||
this is shown, for example, in an upstream's UPnP status page.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# gives networking.firewall value for a given "${port}" = portCfg.
|
||||
firewallConfigForPort = port: portCfg:
|
||||
# any form of visibility means we need to open the firewall
|
||||
lib.mkIf (portCfg.visibleTo.lan || portCfg.visibleTo.wan || portCfg.visibleTo.ovpn) {
|
||||
allowedTCPPorts = lib.optional (lib.elem "tcp" portCfg.protocol) (lib.toInt port);
|
||||
allowedUDPPorts = lib.optional (lib.elem "udp" portCfg.protocol) (lib.toInt port);
|
||||
};
|
||||
in
|
||||
{
|
||||
options = with lib; {
|
||||
sane.ports = {
|
||||
openFirewall = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
};
|
||||
openUpnp = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
};
|
||||
upnpRenewInterval = mkOption {
|
||||
default = "1hr";
|
||||
type = types.str;
|
||||
description = "how frequently to renew UPnP leases";
|
||||
};
|
||||
upnpLeaseDuration = mkOption {
|
||||
default = 86400;
|
||||
type = types.int;
|
||||
description = "how long to lease UPnP ports for";
|
||||
};
|
||||
|
||||
ports = mkOption {
|
||||
type = types.attrsOf portOpts;
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
(lib.mkIf cfg.openFirewall {
|
||||
networking.firewall = lib.mkMerge (lib.mapAttrsToList firewallConfigForPort cfg.ports);
|
||||
})
|
||||
(lib.mkIf cfg.openUpnp {
|
||||
systemd.services.upnp-forwards = {
|
||||
description = "forward ports from upstream gateway to this host";
|
||||
serviceConfig.Type = "oneshot";
|
||||
restartTriggers = [(builtins.toJSON cfg)];
|
||||
|
||||
after = [ "network.target" ];
|
||||
script =
|
||||
let
|
||||
portFwd = "${pkgs.sane-scripts.ip-port-forward}/bin/sane-ip-port-forward";
|
||||
forwardsPerCfg = lib.mapAttrsToList
|
||||
(port: portCfg: lib.optionals portCfg.visibleTo.wan
|
||||
(
|
||||
lib.optional (lib.elem "udp" portCfg.protocol) "udp:${port}:${portCfg.description}"
|
||||
++ lib.optional (lib.elem "tcp" portCfg.protocol) "tcp:${port}:${portCfg.description}"
|
||||
)
|
||||
)
|
||||
cfg.ports;
|
||||
forwards = lib.flatten forwardsPerCfg;
|
||||
in ''
|
||||
${portFwd} -v -d ${builtins.toString cfg.upnpLeaseDuration} \
|
||||
${lib.escapeShellArgs forwards}
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.timers.upnp-forwards = {
|
||||
wantedBy = [ "network.target" ];
|
||||
timerConfig = {
|
||||
OnStartupSec = "1min";
|
||||
OnUnitActiveSec = cfg.upnpRenewInterval;
|
||||
};
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
@@ -104,6 +104,19 @@ let
|
||||
the secret will have same owner as the user under which the program is enabled.
|
||||
'';
|
||||
};
|
||||
configOption = mkOption {
|
||||
type = types.raw;
|
||||
default = mkOption {
|
||||
type = types.submodule {};
|
||||
default = {};
|
||||
};
|
||||
description = ''
|
||||
declare any other options the program may be configured with.
|
||||
you probably want this to be a submodule.
|
||||
the option *definitions* can be set with `sane.programs."foo".config = ...`.
|
||||
'';
|
||||
};
|
||||
config = config.configOption;
|
||||
};
|
||||
|
||||
config = {
|
||||
@@ -132,9 +145,10 @@ let
|
||||
sane.users = mapAttrs (user: en: optionalAttrs en {
|
||||
inherit (p) persist;
|
||||
fs = mkMerge [
|
||||
p.fs
|
||||
# make every fs entry wanted by system boot:
|
||||
(mapAttrs (_path: sane-lib.fs.wanted) p.fs)
|
||||
# link every secret into the fs:
|
||||
(mapAttrs
|
||||
# link every secret into the fs
|
||||
# TODO: user the user's *actual* home directory, don't guess.
|
||||
(homePath: _src: sane-lib.fs.wantedSymlinkTo "/run/secrets/home/${user}/${homePath}")
|
||||
p.secrets
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user