Compare commits
461 Commits
wip-nix-fa
...
wip-mpv-sy
Author | SHA1 | Date | |
---|---|---|---|
72c7287445 | |||
b715fd346f | |||
1b9b0ac0f6 | |||
79c4e2c405 | |||
17a3f90825 | |||
03bec6aab2 | |||
3aba91b360 | |||
907933612d | |||
0db546bf82 | |||
b4877a488e | |||
4b3975367a | |||
38c8d96e5a | |||
28110c3e85 | |||
43aa498ff9 | |||
f7e4504764 | |||
4942fa8a38 | |||
7ab148ea58 | |||
0dfeec3260 | |||
eb2317a743 | |||
1a0ef28377 | |||
7c3ad85d75 | |||
7766e1cec1 | |||
158e674f83 | |||
410097480f | |||
f5fadbe4cf | |||
a0550660e7 | |||
bad6c353ed | |||
a814832e48 | |||
a4312f1494 | |||
747032d9a4 | |||
9b2e35b93f | |||
d2751237c1 | |||
ae87160de3 | |||
a90a213cc0 | |||
24c04b8fc0 | |||
d0b022d1c6 | |||
9d9791814a | |||
b85d4b20f8 | |||
331e673589 | |||
bbb93600b7 | |||
c0de54c11a | |||
0d29722443 | |||
1c2a375b6d | |||
b6840a3ed4 | |||
74e994598e | |||
856b6fcd7a | |||
2404fb66f3 | |||
cd6a91e995 | |||
89d4b0ae0b | |||
ade680d9d2 | |||
6d4a43fa0d | |||
d3ad661970 | |||
c9632b05f9 | |||
1e7de43da8 | |||
eff37765ae | |||
a65673847a | |||
930c5e2412 | |||
aff2a78ec3 | |||
f01758503c | |||
e855be4796 | |||
701e10b121 | |||
eadb2057d9 | |||
5ed29ceb47 | |||
725ab13628 | |||
32e691b85b | |||
0108502055 | |||
6c5b32aac2 | |||
f59dd99470 | |||
55c8a98c33 | |||
7bb67391ae | |||
c6a1f310a0 | |||
1d494513a9 | |||
fb79ca4c8e | |||
3cf42db7dc | |||
aba5eee837 | |||
5cd9f34884 | |||
2cabe51956 | |||
cb8e9b7a23 | |||
4eb6b5735e | |||
5d3899959b | |||
ad951ad919 | |||
5ecabc57bf | |||
48a4c1bd26 | |||
1f47c5ba2e | |||
febedb9323 | |||
aed5ea4b2e | |||
4e74ba5bab | |||
03fbb780b2 | |||
9c0b175260 | |||
e62be121e2 | |||
774066e53c | |||
86400f45d6 | |||
ddef2d0bfc | |||
0172aa0b69 | |||
ce991c8887 | |||
92d8d42997 | |||
1c4ef84ec7 | |||
a820ae57c0 | |||
89f913cadc | |||
d14fda2e62 | |||
f680a4a25c | |||
7c461cee2f | |||
47d37b4ce5 | |||
a1cc045837 | |||
72dd556b72 | |||
ff9e1111b3 | |||
7f8cae42ff | |||
5b83d4d944 | |||
f16a68f5bb | |||
6646a21089 | |||
4bf43d884a | |||
46fe6c690b | |||
dd7b1dae5f | |||
2e58353b0e | |||
f65d3d04dc | |||
6102a0301d | |||
39de5b84c2 | |||
5205251f6f | |||
8c48adefa5 | |||
db2801c652 | |||
4418c16967 | |||
8008fd35cb | |||
36ea5b53ad | |||
552d14b1b5 | |||
c404c8b2ae | |||
d129ae2c03 | |||
58341b75f2 | |||
373388c5b8 | |||
8d45aad534 | |||
a783bc9577 | |||
267d374b19 | |||
e67ce7576b | |||
ce770dbea9 | |||
e7a65abd0b | |||
702a6cc7fa | |||
f889543aa5 | |||
98073f5e19 | |||
96c330813f | |||
a6d9c62bcf | |||
8ff34d8518 | |||
e11dd0ecb0 | |||
3b6dfea2d0 | |||
22254db74c | |||
a316c87db6 | |||
fe2fb40565 | |||
cd63fdb510 | |||
1e25f37774 | |||
cdac23211c | |||
e6c00e6215 | |||
fff9d69e3e | |||
4fa7e6113d | |||
16ca71188f | |||
c5c37e79ac | |||
d2f6648bce | |||
5c9c7f8073 | |||
218072b2fe | |||
d4f217a4f5 | |||
40f6f88a64 | |||
fbbb09322a | |||
e7153ce4a1 | |||
b13e7c38c7 | |||
058c95bb2c | |||
9b793ef4b8 | |||
1417497001 | |||
db12e03f64 | |||
dee4866737 | |||
81a6c53c26 | |||
9afd9725d1 | |||
384bc9e816 | |||
452619dbfc | |||
6c6e10e470 | |||
dcdf58e1ab | |||
48b2280f2e | |||
8bedc860ae | |||
cbecdc4a95 | |||
e1001f57c5 | |||
291e704477 | |||
d199e9df99 | |||
2336767059 | |||
63af94383b | |||
05b37669e3 | |||
ea9768c6ab | |||
38353dbc29 | |||
ef4a8e1989 | |||
acc9a9cb48 | |||
0335b89a12 | |||
0a6b0cbec7 | |||
df2310d590 | |||
70b5c57b50 | |||
c28ac38652 | |||
52133fde30 | |||
098cd2051e | |||
691a7d7ff7 | |||
c7c2785ad8 | |||
4c1a7fc910 | |||
3c43fba878 | |||
7904957544 | |||
b25df1d997 | |||
d08f318e4b | |||
f655c31d77 | |||
288d57e5d5 | |||
f669f2bfe8 | |||
5554ad9fe2 | |||
6595d177be | |||
d194abb4bf | |||
eaf45e2366 | |||
66e04857b6 | |||
fa0dcdc5be | |||
23b87a283a | |||
849ca59f68 | |||
5e1a6062af | |||
8dacb93861 | |||
eafabe87c4 | |||
4510352c07 | |||
430592632c | |||
ac22b36d78 | |||
4439491bf0 | |||
56aca78d84 | |||
546482dc80 | |||
2f07fff084 | |||
294563c655 | |||
f6c3557b8c | |||
7513811111 | |||
eabd113262 | |||
01fa9919fd | |||
56a2c4e49f | |||
bf953fbdb5 | |||
4f2d0f2e56 | |||
500c989e61 | |||
08ee0375cc | |||
afd1a42ec7 | |||
21691fc2fd | |||
c1edf96ce0 | |||
21714849cf | |||
379f3ef9e0 | |||
c07c23a9f8 | |||
f44a4c84ee | |||
6822dad9c0 | |||
9f8e42ef92 | |||
bda374db13 | |||
180a217744 | |||
f13ece2212 | |||
00ab28fcac | |||
bb18f7355a | |||
0905a658ad | |||
90b9d00f37 | |||
40effc08d6 | |||
73a2c9d923 | |||
d93380938d | |||
573a50fedc | |||
f8797a77ff | |||
92115709f6 | |||
e6111c9d5e | |||
975df698a4 | |||
ce8c4a4f6f | |||
df33576090 | |||
4d6eb705eb | |||
fd70b6acbf | |||
1d3576b892 | |||
30d49dc3c3 | |||
8e0031e770 | |||
7a50fcf566 | |||
c453dbac8e | |||
053b8cf737 | |||
8af962c3a6 | |||
9ea39799a5 | |||
e695459b40 | |||
f2e760710d | |||
dc70ed8bd8 | |||
b41c249830 | |||
8f986e4616 | |||
2b73ebb4c1 | |||
bffcaa668e | |||
ebb037bd48 | |||
df98ef30e0 | |||
e45d4d6ae7 | |||
f3568b3ffc | |||
9eaf4d71b2 | |||
3200188a32 | |||
a4ab60b836 | |||
3282b40e9b | |||
39411164af | |||
c0a94995a5 | |||
f4b5d3a70a | |||
07373b5e6b | |||
7281b94e23 | |||
0e83742096 | |||
7d8205352c | |||
c4994162e1 | |||
24a211bd3d | |||
3cf651b212 | |||
cb1c76a0db | |||
27c12edec0 | |||
b41320ffb3 | |||
ac41cfcd42 | |||
62cbc65f12 | |||
ccb856faf5 | |||
d7f4438371 | |||
f44c3f2e1f | |||
bb300a4eb5 | |||
fd4842ab5b | |||
1cdc3b8bda | |||
531ea11b3d | |||
041ce0654e | |||
526a02bb73 | |||
1d0458ab10 | |||
4358f9471e | |||
cb3a1fb3fe | |||
72d52f9239 | |||
219fe67f34 | |||
b36d224b85 | |||
90e3c33536 | |||
a9419b7351 | |||
f0d0343b32 | |||
bd27f3a015 | |||
6a3e632335 | |||
a4c4b0575c | |||
4730a65008 | |||
63d95edcbe | |||
687e72897b | |||
0e84744115 | |||
1ee38d3aec | |||
5f4e421ab9 | |||
471339d237 | |||
085232f18c | |||
18c7fc17fd | |||
a7567dfbe6 | |||
bc0660b623 | |||
cf86b4a67c | |||
d39bed46b5 | |||
525450e21a | |||
a4ee820921 | |||
4b5d6b16e1 | |||
41a141dba6 | |||
d5811f142d | |||
4d6d79cc81 | |||
53d76920e4 | |||
db892273ac | |||
65e206afde | |||
6765fe8d7d | |||
955119e07b | |||
daddf9314a | |||
e59123fd62 | |||
d43cc6c61c | |||
a8bc77d40c | |||
dfc768e2e6 | |||
3544b4b132 | |||
c86afca795 | |||
5f3ef37050 | |||
3b8252fe43 | |||
1e6e4d2644 | |||
60c447b6c4 | |||
4fc2ffef56 | |||
9416ef1ff6 | |||
b1dff9bfa8 | |||
0003acc091 | |||
0630037f86 | |||
9513680538 | |||
0affeb098a | |||
979d07d693 | |||
fd072883dc | |||
ed87792f9b | |||
8821b3ca7d | |||
5e5a1fbaae | |||
d2f470dc74 | |||
7933ef41a2 | |||
6b45589e54 | |||
b04357c9de | |||
4b04cbc078 | |||
1498e364b2 | |||
0aaa3eaaeb | |||
3ac6b92c18 | |||
c747855810 | |||
711865018d | |||
f33fcc2018 | |||
96ec0106ee | |||
0c4d7761d3 | |||
fe10640821 | |||
1f208083be | |||
6ec3126321 | |||
8029744c90 | |||
1fcf0bfcb1 | |||
a3ae650273 | |||
7e0bef549f | |||
3b603519ff | |||
f69ca166f4 | |||
a45e42910d | |||
3dd1d18dcd | |||
28cb705bd4 | |||
7fa1dbc5d5 | |||
8b7575c205 | |||
52e9902fa1 | |||
ab765a81af | |||
a7bd831ad8 | |||
063d99dd73 | |||
0d48c462cb | |||
f4ec09f010 | |||
a7ef9fc0b8 | |||
a40cefc8a5 | |||
f55bb3518f | |||
3d16aa62ea | |||
2548cfeadc | |||
90acbf716d | |||
4d98593b3f | |||
db89ac88f0 | |||
bba149c670 | |||
c056984003 | |||
2324d75165 | |||
9296b7731b | |||
75e9ce509e | |||
95c95d6f53 | |||
fca23e661a | |||
9a7ebbd9d3 | |||
56b00d998e | |||
01ef182073 | |||
b6daeddfa2 | |||
c6e956f3d2 | |||
f9510e5d24 | |||
2e737c2ab1 | |||
82368eb45a | |||
65fb9e1d57 | |||
b02ae7ef74 | |||
37ddb2ae17 | |||
81e02e2885 | |||
4a3f59468c | |||
daab5939e7 | |||
e7430c41f9 | |||
5849e75577 | |||
296123651c | |||
7f0d5e7810 | |||
7af928a6d2 | |||
b73569d675 | |||
50ee15ef2b | |||
9764d5f095 | |||
43386f3ba5 | |||
a3a6278a59 | |||
083f743c1f | |||
626ad97005 | |||
6253d1799a | |||
d8a8038cae | |||
7fd56b63cb | |||
7a65bd36c7 | |||
bd4eeeeb3b | |||
7c22b59b9d | |||
9e504676bd | |||
b515127101 | |||
40e30cf2f8 | |||
812c0c8029 | |||
7ca5ae84b7 | |||
a4248fd5cc | |||
70229e0839 | |||
cd303a76bc | |||
e43aa3bb8b | |||
6c2d80715c | |||
db8456f152 | |||
d912190db5 | |||
c380f61bea | |||
ff8cfc9372 | |||
b302113fc0 | |||
9749ff0442 | |||
0d97191f74 |
@@ -15,8 +15,9 @@ the only hard dependency for my exported pkgs/modules should be [nixpkgs][nixpkg
|
|||||||
building [hosts/](./hosts/) will require [sops][sops].
|
building [hosts/](./hosts/) will require [sops][sops].
|
||||||
|
|
||||||
you might specifically be interested in these files (elaborated further in #key-points-of-interest):
|
you might specifically be interested in these files (elaborated further in #key-points-of-interest):
|
||||||
- [`sxmo-utils`](./pkgs/additional/sxmo-utils/default.nix)
|
- ~~[`sxmo-utils`](./pkgs/additional/sxmo-utils/default.nix)~~
|
||||||
- [example SXMO deployment](./hosts/modules/gui/sxmo/default.nix)
|
- ~~[example SXMO deployment](./hosts/modules/gui/sxmo/default.nix)~~
|
||||||
|
- these files will remain until my config settles down, but i no longer use or maintain SXMO.
|
||||||
- [my implementation of impermanence](./modules/persist/default.nix)
|
- [my implementation of impermanence](./modules/persist/default.nix)
|
||||||
- my way of deploying dotfiles/configuring programs per-user:
|
- my way of deploying dotfiles/configuring programs per-user:
|
||||||
- [modules/fs/](./modules/fs/default.nix)
|
- [modules/fs/](./modules/fs/default.nix)
|
||||||
|
20
TODO.md
20
TODO.md
@@ -1,9 +1,14 @@
|
|||||||
## BUGS
|
## BUGS
|
||||||
|
- Signal restart loop drains battery
|
||||||
|
- decrease s6 restart time?
|
||||||
|
- mpv `player-mode=pseudo-gui` swallows all loggin
|
||||||
- ringer (i.e. dino incoming call) doesn't prevent moby from sleeping
|
- ringer (i.e. dino incoming call) doesn't prevent moby from sleeping
|
||||||
|
- sway mouse/kb hotplug doesn't work
|
||||||
- `nix` operations from lappy hang when `desko` is unreachable
|
- `nix` operations from lappy hang when `desko` is unreachable
|
||||||
- could at least direct the cache to `http://desko-hn:5001`
|
- could at least direct the cache to `http://desko-hn:5001`
|
||||||
|
|
||||||
## REFACTORING:
|
## REFACTORING:
|
||||||
|
- REMOVE DEPRECATED `crypt` from sftpgo_auth_hook
|
||||||
- consolidate ~/dev and ~/ref
|
- consolidate ~/dev and ~/ref
|
||||||
- ~/dev becomes a link to ~/ref/cat/mine
|
- ~/dev becomes a link to ~/ref/cat/mine
|
||||||
- fold hosts/common/home/ssh.nix -> hosts/common/users/colin.nix
|
- fold hosts/common/home/ssh.nix -> hosts/common/users/colin.nix
|
||||||
@@ -11,16 +16,15 @@
|
|||||||
### sops/secrets
|
### sops/secrets
|
||||||
- rework secrets to leverage `sane.fs`
|
- rework secrets to leverage `sane.fs`
|
||||||
- remove sops activation script as it's covered by my systemd sane.fs impl
|
- remove sops activation script as it's covered by my systemd sane.fs impl
|
||||||
|
- user secrets could just use `gocryptfs`, like with ~/private?
|
||||||
|
- can gocryptfs support nested filesystems, each with different perms (for desko, moby, etc)?
|
||||||
|
|
||||||
### roles
|
### roles
|
||||||
- allow any host to take the role of `uninsane.org`
|
- allow any host to take the role of `uninsane.org`
|
||||||
- will make it easier to test new services?
|
- will make it easier to test new services?
|
||||||
|
|
||||||
### upstreaming
|
### upstreaming
|
||||||
- split out a sxmo module usable by NUR consumers
|
|
||||||
- bump nodejs version in lemmy-ui
|
|
||||||
- add updateScripts to all my packages in nixpkgs
|
- add updateScripts to all my packages in nixpkgs
|
||||||
- fix lightdm-mobile-greeter for newer libhandy
|
|
||||||
- REVIEW/integrate jellyfin dataDir config: <https://github.com/NixOS/nixpkgs/pull/233617>
|
- REVIEW/integrate jellyfin dataDir config: <https://github.com/NixOS/nixpkgs/pull/233617>
|
||||||
|
|
||||||
#### upstreaming to non-nixpkgs repos
|
#### upstreaming to non-nixpkgs repos
|
||||||
@@ -47,6 +51,8 @@
|
|||||||
- limit access to `~/knowledge/secrets` through an agent that requires GUI approval, so a firefox exploit can't steal all my logins
|
- limit access to `~/knowledge/secrets` through an agent that requires GUI approval, so a firefox exploit can't steal all my logins
|
||||||
- port sane-sandboxed to a compiled language (hare?)
|
- port sane-sandboxed to a compiled language (hare?)
|
||||||
- it adds like 50-70ms launch time _on my laptop_. i'd hate to know how much that is on the pinephone.
|
- it adds like 50-70ms launch time _on my laptop_. i'd hate to know how much that is on the pinephone.
|
||||||
|
- remove /run/wrappers from the sandbox path
|
||||||
|
- they're mostly useless when using no-new-privs, just an opportunity to forget to specify deps
|
||||||
- make dconf stuff less monolithic
|
- make dconf stuff less monolithic
|
||||||
- i.e. per-app dconf profiles for those which need it. possible static config.
|
- i.e. per-app dconf profiles for those which need it. possible static config.
|
||||||
- canaries for important services
|
- canaries for important services
|
||||||
@@ -54,6 +60,11 @@
|
|||||||
- integrate `nix check` into Gitea actions?
|
- integrate `nix check` into Gitea actions?
|
||||||
|
|
||||||
### user experience
|
### user experience
|
||||||
|
- xdg-desktop-portal shouldn't kill children on exit
|
||||||
|
- *maybe* a job for `setsid -f`?
|
||||||
|
- replace starship prompt with something more efficient
|
||||||
|
- watch `forkstat`: it does way too much
|
||||||
|
- cleanup waybar so that it's not invoking playerctl every 2 seconds
|
||||||
- install apps:
|
- install apps:
|
||||||
- display QR codes for WiFi endpoints: <https://linuxphoneapps.org/apps/noappid.wisperwind.wifi2qr/>
|
- display QR codes for WiFi endpoints: <https://linuxphoneapps.org/apps/noappid.wisperwind.wifi2qr/>
|
||||||
- shopping list (not in nixpkgs): <https://linuxphoneapps.org/apps/ro.hume.cosmin.shoppinglist/>
|
- shopping list (not in nixpkgs): <https://linuxphoneapps.org/apps/ro.hume.cosmin.shoppinglist/>
|
||||||
@@ -76,6 +87,7 @@
|
|||||||
|
|
||||||
#### moby
|
#### moby
|
||||||
- fix cpuidle (gets better power consumption): <https://xnux.eu/log/077.html>
|
- fix cpuidle (gets better power consumption): <https://xnux.eu/log/077.html>
|
||||||
|
- moby: tune keyboard layout
|
||||||
- SwayNC:
|
- SwayNC:
|
||||||
- don't show MPRIS if no players detected
|
- don't show MPRIS if no players detected
|
||||||
- this is a problem of playerctld, i guess
|
- this is a problem of playerctld, i guess
|
||||||
@@ -97,6 +109,7 @@
|
|||||||
- RSS: integrate a paywall bypass
|
- RSS: integrate a paywall bypass
|
||||||
- e.g. self-hosted [ladder](https://github.com/everywall/ladder) (like 12ft.io)
|
- e.g. self-hosted [ladder](https://github.com/everywall/ladder) (like 12ft.io)
|
||||||
- neovim: set up language server (lsp; rnix-lsp; nvim-lspconfig)
|
- neovim: set up language server (lsp; rnix-lsp; nvim-lspconfig)
|
||||||
|
- neovim: integrate LLMs
|
||||||
- Helix: make copy-to-system clipboard be the default
|
- Helix: make copy-to-system clipboard be the default
|
||||||
- firefox/librewolf: persist history
|
- firefox/librewolf: persist history
|
||||||
- just not cookies or tabs
|
- just not cookies or tabs
|
||||||
@@ -114,7 +127,6 @@
|
|||||||
|
|
||||||
### perf
|
### perf
|
||||||
- debug nixos-rebuild times
|
- debug nixos-rebuild times
|
||||||
- i bet sane.programs adds a LOT of time, with how it automatically creates an attrs for EVERY package in nixpkgs.
|
|
||||||
- add `pkgs.impure-cached.<foo>` package set to build things with ccache enabled
|
- add `pkgs.impure-cached.<foo>` package set to build things with ccache enabled
|
||||||
- every package here can be auto-generated, and marked with some env var so that it doesn't pollute the pure package set
|
- every package here can be auto-generated, and marked with some env var so that it doesn't pollute the pure package set
|
||||||
- would be super handy for package prototyping!
|
- would be super handy for package prototyping!
|
||||||
|
241
flake.lock
generated
241
flake.lock
generated
@@ -1,5 +1,79 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"flake-compat": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1688025799,
|
||||||
|
"narHash": "sha256-ktpB4dRtnksm9F5WawoIkEneh1nrEvuxb5lJFt1iOyw=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "8bf105319d44f6b9f0d764efa4fdef9f1cc9ba1c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": [
|
||||||
|
"nixpkgs-wayland",
|
||||||
|
"nix-eval-jobs",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1701473968,
|
||||||
|
"narHash": "sha256-YcVE5emp1qQ8ieHUnxt1wCZCC3ZfAS+SRRWZ2TMda7E=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "34fed993f1674c8d06d58b37ce1e0fe5eebcb9f5",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710146030,
|
||||||
|
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lib-aggregate": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1711886936,
|
||||||
|
"narHash": "sha256-D2WENp9GuaCostvNcQ7vElekk0V5cuMdnFZ7NfRhVrQ=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "lib-aggregate",
|
||||||
|
"rev": "9c06929b83e57c18d125f1105ba6a423f24083d2",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "lib-aggregate",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"mobile-nixos": {
|
"mobile-nixos": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -17,13 +91,87 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nix-eval-jobs": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"nix-github-actions": "nix-github-actions",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"treefmt-nix": "treefmt-nix"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1705242886,
|
||||||
|
"narHash": "sha256-TLj334vRwFtSym3m+NnKcNCnKKPNoTC/TDZL40vmOso=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-eval-jobs",
|
||||||
|
"rev": "6b03a93296faf174b97546fd573c8b379f523a8d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-eval-jobs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nix-github-actions": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs-wayland",
|
||||||
|
"nix-eval-jobs",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1701208414,
|
||||||
|
"narHash": "sha256-xrQ0FyhwTZK6BwKhahIkUVZhMNk21IEI1nUcWSONtpo=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-github-actions",
|
||||||
|
"rev": "93e39cc1a087d65bcf7a132e75a650c44dd2b734",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-github-actions",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1703134684,
|
||||||
|
"narHash": "sha256-SQmng1EnBFLzS7WSRyPM9HgmZP2kLJcPAz+Ug/nug6o=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "d6863cbcbbb80e71cecfc03356db1cda38919523",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1711846064,
|
||||||
|
"narHash": "sha256-cqfX0QJNEnge3a77VnytM0Q6QZZ0DziFXt6tSCV8ZSc=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"rev": "90b1a963ff84dc532db92f678296ff2499a60a87",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs-next-unpatched": {
|
"nixpkgs-next-unpatched": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1708992120,
|
"lastModified": 1712383280,
|
||||||
"narHash": "sha256-t/8QV+lEroW5fK44w5oEUalIM0eYYVGs833AHDCIl4s=",
|
"narHash": "sha256-YL8miM11o/jMqOwt5DsdyhPgh/JgCl1kOIzvX7ukniY=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "6daf4de0662e1d895d220a4a4ddb356eb000abe9",
|
"rev": "7c74352f2f7eca1925729f5c9c80cb89df8e74a2",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -35,11 +183,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-stable": {
|
"nixpkgs-stable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1708819810,
|
"lastModified": 1711819797,
|
||||||
"narHash": "sha256-1KosU+ZFXf31GPeCBNxobZWMgHsSOJcrSFA6F2jhzdE=",
|
"narHash": "sha256-tNeB6emxj74Y6ctwmsjtMlzUMn458sBmwnD35U5KIM4=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "89a2a12e6c8c6a56c72eb3589982c8e2f89c70ea",
|
"rev": "2b4e3ca0091049c6fbb4908c66b05b77eaef9f0c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -51,11 +199,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unpatched": {
|
"nixpkgs-unpatched": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1708995544,
|
"lastModified": 1712398506,
|
||||||
"narHash": "sha256-YJgLopKOKVTggnKzjX4OiAS22hx/vNv397DcsAyTZgY=",
|
"narHash": "sha256-oopwPeBKBXQEw2BlyK2jEs2farZ5uMjAZU7H4FpGuGE=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "5bd8df40204f47a12263f3614c72cd5b6832a9a0",
|
"rev": "c58702222e0a29fd01cc42d70737d699995f6389",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -65,11 +213,35 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixpkgs-wayland": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"lib-aggregate": "lib-aggregate",
|
||||||
|
"nix-eval-jobs": "nix-eval-jobs",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs-unpatched"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1712237761,
|
||||||
|
"narHash": "sha256-NoMBBCADTms3yx5BL+sbc7vfDivNiYULO6t9GBAsPt0=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs-wayland",
|
||||||
|
"rev": "9b77653338f52da4b498abdf4835efb6ff6e453e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs-wayland",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"mobile-nixos": "mobile-nixos",
|
"mobile-nixos": "mobile-nixos",
|
||||||
"nixpkgs-next-unpatched": "nixpkgs-next-unpatched",
|
"nixpkgs-next-unpatched": "nixpkgs-next-unpatched",
|
||||||
"nixpkgs-unpatched": "nixpkgs-unpatched",
|
"nixpkgs-unpatched": "nixpkgs-unpatched",
|
||||||
|
"nixpkgs-wayland": "nixpkgs-wayland",
|
||||||
"sops-nix": "sops-nix",
|
"sops-nix": "sops-nix",
|
||||||
"uninsane-dot-org": "uninsane-dot-org"
|
"uninsane-dot-org": "uninsane-dot-org"
|
||||||
}
|
}
|
||||||
@@ -82,11 +254,11 @@
|
|||||||
"nixpkgs-stable": "nixpkgs-stable"
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1708987867,
|
"lastModified": 1711855048,
|
||||||
"narHash": "sha256-k2lDaDWNTU5sBVHanYzjDKVDmk29RHIgdbbXu5sdzBA=",
|
"narHash": "sha256-HxegAPnQJSC4cbEbF4Iq3YTlFHZKLiNTk8147EbLdGg=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "a1c8de14f60924fafe13aea66b46157f0150f4cf",
|
"rev": "99b1e37f9fc0960d064a7862eb7adfb92e64fa10",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -95,6 +267,43 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"treefmt-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs-wayland",
|
||||||
|
"nix-eval-jobs",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1702979157,
|
||||||
|
"narHash": "sha256-RnFBbLbpqtn4AoJGXKevQMCGhra4h6G2MPcuTSZZQ+g=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"rev": "2961375283668d867e64129c22af532de8e77734",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"uninsane-dot-org": {
|
"uninsane-dot-org": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -102,11 +311,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1707981105,
|
"lastModified": 1711371733,
|
||||||
"narHash": "sha256-YCU1eNslBHabjP+OCY+BxPycEFO9SRUts10MrN9QORE=",
|
"narHash": "sha256-+brjlMyLVnVADY31sN82Ap0IsPE2WZEwHUd94sY6BXI=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "bb10cd8853d05191e4d62947d93687c462e92c30",
|
"rev": "b9502e6f190752d327f8cee7fa4b139094bd7c16",
|
||||||
"revCount": 235,
|
"revCount": 237,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.uninsane.org/colin/uninsane"
|
"url": "https://git.uninsane.org/colin/uninsane"
|
||||||
},
|
},
|
||||||
|
77
flake.nix
77
flake.nix
@@ -48,6 +48,11 @@
|
|||||||
# nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=nixos-staging-next";
|
# nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=nixos-staging-next";
|
||||||
nixpkgs-next-unpatched.url = "github:nixos/nixpkgs?ref=staging-next";
|
nixpkgs-next-unpatched.url = "github:nixos/nixpkgs?ref=staging-next";
|
||||||
|
|
||||||
|
nixpkgs-wayland = {
|
||||||
|
url = "github:nix-community/nixpkgs-wayland";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs-unpatched";
|
||||||
|
};
|
||||||
|
|
||||||
mobile-nixos = {
|
mobile-nixos = {
|
||||||
# <https://github.com/nixos/mobile-nixos>
|
# <https://github.com/nixos/mobile-nixos>
|
||||||
# only used for building disk images, not relevant after deployment
|
# only used for building disk images, not relevant after deployment
|
||||||
@@ -76,6 +81,7 @@
|
|||||||
self,
|
self,
|
||||||
nixpkgs-unpatched,
|
nixpkgs-unpatched,
|
||||||
nixpkgs-next-unpatched ? nixpkgs-unpatched,
|
nixpkgs-next-unpatched ? nixpkgs-unpatched,
|
||||||
|
nixpkgs-wayland,
|
||||||
mobile-nixos,
|
mobile-nixos,
|
||||||
sops-nix,
|
sops-nix,
|
||||||
uninsane-dot-org,
|
uninsane-dot-org,
|
||||||
@@ -97,30 +103,7 @@
|
|||||||
patchNixpkgs = variant: nixpkgs: (import ./nixpatches/flake.nix).outputs {
|
patchNixpkgs = variant: nixpkgs: (import ./nixpatches/flake.nix).outputs {
|
||||||
inherit variant nixpkgs;
|
inherit variant nixpkgs;
|
||||||
self = patchNixpkgs variant nixpkgs;
|
self = patchNixpkgs variant nixpkgs;
|
||||||
} // {
|
};
|
||||||
# sourceInfo includes fields (square brackets for the ones which are not always present):
|
|
||||||
# - [dirtyRev]
|
|
||||||
# - [dirtyShortRev]
|
|
||||||
# - lastModified
|
|
||||||
# - lastModifiedDate
|
|
||||||
# - narHash
|
|
||||||
# - outPath
|
|
||||||
# - [rev]
|
|
||||||
# - [revCount]
|
|
||||||
# - [shortRev]
|
|
||||||
# - submodules
|
|
||||||
#
|
|
||||||
# these values are used within nixpkgs:
|
|
||||||
# - to give a friendly name to the nixos system (`readlink /run/current-system` -> `...nixos-system-desko-24.05.20240227.dirty`)
|
|
||||||
# - to alias `import <nixpkgs>` so that nix uses the system's nixpkgs when called externally (supposedly).
|
|
||||||
#
|
|
||||||
# these values seem to exist both within the `sourceInfo` attrset and at the top-level.
|
|
||||||
# for a list of all implicit flake outputs (which is what these seem to be):
|
|
||||||
# $ nix-repl
|
|
||||||
# > lf .
|
|
||||||
# > <tab>
|
|
||||||
inherit (self) sourceInfo;
|
|
||||||
} // self.sourceInfo;
|
|
||||||
|
|
||||||
nixpkgs' = patchNixpkgs "master" nixpkgs-unpatched;
|
nixpkgs' = patchNixpkgs "master" nixpkgs-unpatched;
|
||||||
nixpkgsCompiledBy = system: nixpkgs'.legacyPackages."${system}";
|
nixpkgsCompiledBy = system: nixpkgs'.legacyPackages."${system}";
|
||||||
@@ -207,9 +190,18 @@
|
|||||||
let
|
let
|
||||||
mobile = (import "${mobile-nixos}/overlay/overlay.nix");
|
mobile = (import "${mobile-nixos}/overlay/overlay.nix");
|
||||||
uninsane = uninsane-dot-org.overlays.default;
|
uninsane = uninsane-dot-org.overlays.default;
|
||||||
|
wayland = final: prev: {
|
||||||
|
# default is to dump the packages into `waylandPkgs` *and* the toplevel.
|
||||||
|
# but i just want the `waylandPkgs` set
|
||||||
|
inherit (nixpkgs-wayland.overlays.default final prev)
|
||||||
|
waylandPkgs
|
||||||
|
new-wayland-protocols #< 2024/03/10: nixpkgs-wayland assumes this will be in the toplevel
|
||||||
|
;
|
||||||
|
};
|
||||||
in
|
in
|
||||||
(mobile final prev)
|
(mobile final prev)
|
||||||
// (uninsane final prev)
|
// (uninsane final prev)
|
||||||
|
// (wayland final prev)
|
||||||
;
|
;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -265,6 +257,8 @@
|
|||||||
pkgs = self.legacyPackages."x86_64-linux";
|
pkgs = self.legacyPackages."x86_64-linux";
|
||||||
sanePkgs = import ./pkgs { inherit pkgs; };
|
sanePkgs = import ./pkgs { inherit pkgs; };
|
||||||
deployScript = host: addr: action: pkgs.writeShellScript "deploy-${host}" ''
|
deployScript = host: addr: action: pkgs.writeShellScript "deploy-${host}" ''
|
||||||
|
set -e
|
||||||
|
|
||||||
host="${host}"
|
host="${host}"
|
||||||
addr="${addr}"
|
addr="${addr}"
|
||||||
action="${if action != null then action else ""}"
|
action="${if action != null then action else ""}"
|
||||||
@@ -278,8 +272,8 @@
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
nix build ".#nixosConfigurations.$host.config.system.build.toplevel" --out-link "./result-$host" "$@"
|
nix build ".#nixosConfigurations.$host.config.system.build.toplevel" --out-link "./build/result-$host" "$@"
|
||||||
storePath="$(readlink ./result-$host)"
|
storePath="$(readlink ./build/result-$host)"
|
||||||
|
|
||||||
# mimic `nixos-rebuild --target-host`, in effect:
|
# mimic `nixos-rebuild --target-host`, in effect:
|
||||||
# - nix-copy-closure ...
|
# - nix-copy-closure ...
|
||||||
@@ -382,6 +376,8 @@
|
|||||||
- or `nix run '.#preDeploy'` to target all hosts
|
- or `nix run '.#preDeploy'` to target all hosts
|
||||||
- `nix run '.#check'`
|
- `nix run '.#check'`
|
||||||
- make sure all systems build; NUR evaluates
|
- make sure all systems build; NUR evaluates
|
||||||
|
- `nix run '.#bench'`
|
||||||
|
- benchmark the eval time of common targets this flake provides
|
||||||
|
|
||||||
specific build targets of interest:
|
specific build targets of interest:
|
||||||
- `nix build '.#imgs.rescue'`
|
- `nix build '.#imgs.rescue'`
|
||||||
@@ -525,6 +521,7 @@
|
|||||||
--option allow-import-from-derivation true \
|
--option allow-import-from-derivation true \
|
||||||
--drv-path --show-trace \
|
--drv-path --show-trace \
|
||||||
-I nixpkgs=${nixpkgs-unpatched} \
|
-I nixpkgs=${nixpkgs-unpatched} \
|
||||||
|
-I nixpkgs-overlays=${./.}/hosts/common/nix/overlay \
|
||||||
-I ../../ \
|
-I ../../ \
|
||||||
| tee # tee to prevent interactive mode
|
| tee # tee to prevent interactive mode
|
||||||
'');
|
'');
|
||||||
@@ -536,7 +533,7 @@
|
|||||||
checkHost = host: let
|
checkHost = host: let
|
||||||
shellHost = pkgs.lib.replaceStrings [ "-" ] [ "_" ] host;
|
shellHost = pkgs.lib.replaceStrings [ "-" ] [ "_" ] host;
|
||||||
in ''
|
in ''
|
||||||
nix build -v '.#nixosConfigurations.${host}.config.system.build.toplevel' --out-link ./result-${host} -j2 "$@"
|
nix build -v '.#nixosConfigurations.${host}.config.system.build.toplevel' --out-link ./build/result-${host} -j2 "$@"
|
||||||
RC_${shellHost}=$?
|
RC_${shellHost}=$?
|
||||||
'';
|
'';
|
||||||
in builtins.toString (pkgs.writeShellScript
|
in builtins.toString (pkgs.writeShellScript
|
||||||
@@ -585,7 +582,31 @@
|
|||||||
check.rescue = {
|
check.rescue = {
|
||||||
type = "app";
|
type = "app";
|
||||||
program = builtins.toString (pkgs.writeShellScript "check-rescue" ''
|
program = builtins.toString (pkgs.writeShellScript "check-rescue" ''
|
||||||
nix build -v '.#imgs.rescue' --out-link ./result-rescue-img -j2
|
nix build -v '.#imgs.rescue' --out-link ./build/result-rescue-img -j2
|
||||||
|
'');
|
||||||
|
};
|
||||||
|
|
||||||
|
bench = {
|
||||||
|
type = "app";
|
||||||
|
program = builtins.toString (pkgs.writeShellScript "bench" ''
|
||||||
|
doBench() {
|
||||||
|
attrPath="$1"
|
||||||
|
shift
|
||||||
|
echo -n "benchmarking eval of '$attrPath'... "
|
||||||
|
/run/current-system/sw/bin/time -f "%e sec" -o /dev/stdout \
|
||||||
|
nix eval --no-eval-cache --quiet --raw ".#$attrPath" --apply 'result: if result != null then "" else "unexpected null"' $@ 2> /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -n "$1" ]; then
|
||||||
|
doBench "$@"
|
||||||
|
else
|
||||||
|
doBench hostConfigs
|
||||||
|
doBench hostConfigs.lappy
|
||||||
|
doBench hostConfigs.lappy.sane.programs
|
||||||
|
doBench hostConfigs.lappy.sane.users.colin
|
||||||
|
doBench hostConfigs.lappy.sane.fs
|
||||||
|
doBench hostConfigs.lappy.environment.systemPackages
|
||||||
|
fi
|
||||||
'');
|
'');
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./fs.nix
|
./fs.nix
|
||||||
./polyfill.nix
|
|
||||||
];
|
];
|
||||||
|
|
||||||
sane.roles.client = true;
|
sane.roles.client = true;
|
||||||
|
@@ -1,41 +0,0 @@
|
|||||||
# doesn't actually *enable* anything,
|
|
||||||
# but sets up any modules such that if they *were* enabled, they'll act as expected.
|
|
||||||
{ pkgs, ... }:
|
|
||||||
{
|
|
||||||
sane.gui.sxmo = {
|
|
||||||
noidle = true; #< power button requires 1s hold, which makes it impractical to be dealing with.
|
|
||||||
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";
|
|
||||||
# N.B.: thinkpad's power button requires a full second press to do anything
|
|
||||||
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_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";
|
|
||||||
# if sxmo doesn't know the device, it can't decide whether to use one_button or three_button mode
|
|
||||||
# and so it just wouldn't handle any button inputs (sxmo_hook_inputhandler.sh not on path)
|
|
||||||
SXMO_DEVICE_NAME = "three_button_touchscreen";
|
|
||||||
};
|
|
||||||
package = (pkgs.sxmo-utils.override { preferSystemd = true; }).overrideAttrs (base: {
|
|
||||||
postPatch = (base.postPatch or "") + ''
|
|
||||||
# after volume-button navigation mode, restore full keyboard functionality
|
|
||||||
cp ${./xkb_mobile_normal_buttons} ./configs/xkb/xkb_mobile_normal_buttons
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
@@ -1,12 +1,22 @@
|
|||||||
|
# tow-boot: <https://tow-boot.org>
|
||||||
|
# docs (pinephone specific): <https://github.com/Tow-Boot/Tow-Boot/tree/development/boards/pine64-pinephoneA64>
|
||||||
|
# LED and button behavior is defined here: <https://github.com/Tow-Boot/Tow-Boot/blob/development/modules/tow-boot/phone-ux.nix>
|
||||||
|
# - hold VOLDOWN: enter recovery mode
|
||||||
|
# - LED will turn aqua instead of yellow
|
||||||
|
# - recovery mode would ordinarily allow a selection of entries, but for pinephone i guess it doesn't do anything?
|
||||||
|
# - hold VOLUP: force it to load the OS from eMMC?
|
||||||
|
# - LED will turn blue instead of yellow
|
||||||
|
# boot LEDs:
|
||||||
|
# - yellow = entered tow-boot
|
||||||
|
# - 10 red flashes => poweroff means tow-boot couldn't boot into the next stage (i.e. distroboot)
|
||||||
|
# - distroboot: <https://source.denx.de/u-boot/u-boot/-/blob/v2022.04/doc/develop/distro.rst>)
|
||||||
{ config, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
{
|
{
|
||||||
# we need space in the GPT header to place tow-boot.
|
# we need space in the GPT header to place tow-boot.
|
||||||
# only actually need 1 MB, but better to over-allocate than under-allocate
|
# only actually need 1 MB, but better to over-allocate than under-allocate
|
||||||
sane.image.extraGPTPadding = 16 * 1024 * 1024;
|
sane.image.extraGPTPadding = 16 * 1024 * 1024;
|
||||||
sane.image.firstPartGap = 0;
|
sane.image.firstPartGap = 0;
|
||||||
system.build.img = pkgs.runCommand "nixos_full-disk-image.img" {} ''
|
sane.image.installBootloader = ''
|
||||||
cp -v ${config.system.build.img-without-firmware}/nixos.img $out
|
dd if=${pkgs.tow-boot-pinephone}/Tow-Boot.noenv.bin of=$out/nixos.img bs=1024 seek=8 conv=notrunc
|
||||||
chmod +w $out
|
|
||||||
dd if=${pkgs.tow-boot-pinephone}/Tow-Boot.noenv.bin of=$out bs=1024 seek=8 conv=notrunc
|
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
@@ -36,10 +36,15 @@
|
|||||||
|
|
||||||
sops.secrets.colin-passwd.neededForUsers = true;
|
sops.secrets.colin-passwd.neededForUsers = true;
|
||||||
|
|
||||||
sane.gui.sxmo.enable = true;
|
# sane.gui.sxmo.enable = true;
|
||||||
# sane.programs.consoleUtils.enableFor.user.colin = false;
|
sane.programs.sway.enableFor.user.colin = true;
|
||||||
# sane.programs.guiApps.enableFor.user.colin = false;
|
sane.programs.swaylock.enableFor.user.colin = false; #< not usable on touch
|
||||||
|
sane.programs.schlock.enableFor.user.colin = true;
|
||||||
|
sane.programs.swayidle.config.actions.screenoff.delay = 300;
|
||||||
|
sane.programs.swayidle.config.actions.screenoff.enable = true;
|
||||||
|
sane.programs.sane-input-handler.enableFor.user.colin = true;
|
||||||
sane.programs.blueberry.enableFor.user.colin = false; # bluetooth manager: doesn't cross compile!
|
sane.programs.blueberry.enableFor.user.colin = false; # bluetooth manager: doesn't cross compile!
|
||||||
|
sane.programs.fcitx5.enableFor.user.colin = false; # does not cross compile
|
||||||
sane.programs.mercurial.enableFor.user.colin = false; # does not cross compile
|
sane.programs.mercurial.enableFor.user.colin = false; # does not cross compile
|
||||||
sane.programs.nvme-cli.enableFor.system = false; # does not cross compile (libhugetlbfs)
|
sane.programs.nvme-cli.enableFor.system = false; # does not cross compile (libhugetlbfs)
|
||||||
|
|
||||||
@@ -52,7 +57,6 @@
|
|||||||
# sane.programs.signal-desktop.config.autostart = true; # TODO: enable once electron stops derping.
|
# sane.programs.signal-desktop.config.autostart = true; # TODO: enable once electron stops derping.
|
||||||
# sane.programs."gnome.geary".config.autostart = true;
|
# sane.programs."gnome.geary".config.autostart = true;
|
||||||
# sane.programs.calls.config.autostart = true;
|
# sane.programs.calls.config.autostart = true;
|
||||||
sane.programs.mpv.config.vo = "wlshm"; #< see hosts/common/programs/mpv.nix for details
|
|
||||||
|
|
||||||
sane.programs.firefox.mime.priority = 300; # prefer other browsers when possible
|
sane.programs.firefox.mime.priority = 300; # prefer other browsers when possible
|
||||||
# HACK/TODO: make `programs.P.env.VAR` behave according to `mime.priority`
|
# HACK/TODO: make `programs.P.env.VAR` behave according to `mime.priority`
|
||||||
@@ -124,42 +128,12 @@
|
|||||||
# enable rotation sensor
|
# enable rotation sensor
|
||||||
hardware.sensor.iio.enable = true;
|
hardware.sensor.iio.enable = true;
|
||||||
|
|
||||||
# inject specialized alsa configs via the environment.
|
# TODO: move elsewhere...
|
||||||
# specifically, this gets the pinephone headphones & internal earpiece working.
|
systemd.services.ModemManager.serviceConfig = {
|
||||||
# see pkgs/patched/alsa-ucm-conf for more info.
|
# N.B.: the extra "" in ExecStart serves to force upstream ExecStart to be ignored
|
||||||
environment.variables.ALSA_CONFIG_UCM2 = "/run/current-system/sw/share/alsa/ucm2";
|
ExecStart = [ "" "${pkgs.modemmanager}/bin/ModemManager --debug" ];
|
||||||
environment.pathsToLink = [ "/share/alsa/ucm2" ];
|
# --debug sets DEBUG level logging: so reset
|
||||||
environment.systemPackages = [
|
ExecStartPost = [ "${pkgs.modemmanager}/bin/mmcli --set-logging=INFO" ];
|
||||||
(pkgs.alsa-ucm-conf-sane.override {
|
|
||||||
# internal speaker has a tendency to break :(
|
|
||||||
preferEarpiece = true;
|
|
||||||
})
|
|
||||||
];
|
|
||||||
systemd = let
|
|
||||||
ucm-env = config.environment.variables.ALSA_CONFIG_UCM2;
|
|
||||||
in {
|
|
||||||
# cribbed from <repo:nixos/mobile-nixos:modules/quirks/audio.nix>
|
|
||||||
|
|
||||||
# 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;
|
|
||||||
|
|
||||||
# pulseaudio
|
|
||||||
# user.services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
|
||||||
# services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: move elsewhere...
|
|
||||||
services.ModemManager.serviceConfig = {
|
|
||||||
# N.B.: the extra "" in ExecStart serves to force upstream ExecStart to be ignored
|
|
||||||
ExecStart = [ "" "${pkgs.modemmanager}/bin/ModemManager --debug" ];
|
|
||||||
# --debug sets DEBUG level logging: so reset
|
|
||||||
ExecStartPost = [ "${pkgs.modemmanager}/bin/mmcli --set-logging=INFO" ];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
services.udev.extraRules = let
|
services.udev.extraRules = let
|
||||||
|
@@ -85,6 +85,7 @@ in
|
|||||||
"lima.sched_timeout_ms=2000"
|
"lima.sched_timeout_ms=2000"
|
||||||
];
|
];
|
||||||
|
|
||||||
services.xserver.displayManager.job.preStart = ensureHWReady;
|
# services.xserver.displayManager.job.preStart = ensureHWReady;
|
||||||
systemd.services.greetd.preStart = ensureHWReady;
|
# systemd.services.greetd.preStart = ensureHWReady;
|
||||||
|
systemd.services.unl0kr.preStart = ensureHWReady;
|
||||||
}
|
}
|
||||||
|
@@ -24,73 +24,22 @@
|
|||||||
backlight = "backlight"; # /sys/class/backlight/*backlight*/brightness
|
backlight = "backlight"; # /sys/class/backlight/*backlight*/brightness
|
||||||
};
|
};
|
||||||
|
|
||||||
sane.gui.sxmo = {
|
sane.programs.alacritty.config.fontSize = 9;
|
||||||
nogesture = true;
|
|
||||||
settings = {
|
|
||||||
### hardware: touch screen
|
|
||||||
SXMO_LISGD_INPUT_DEVICE = "/dev/input/by-path/platform-1c2ac00.i2c-event";
|
|
||||||
# vol and power are detected correctly by upstream
|
|
||||||
|
|
||||||
### preferences
|
sane.programs.sway.config = {
|
||||||
DEFAULT_COUNTRY = "US";
|
font = "pango:monospace 10";
|
||||||
|
mod = "Mod1"; # prefer Alt
|
||||||
|
workspace_layout = "tabbed";
|
||||||
|
};
|
||||||
|
|
||||||
SXMO_AUTOROTATE = "1"; # enable auto-rotation at launch. has no meaning in stock/upstream sxmo-utils
|
sane.programs.waybar.config = {
|
||||||
|
fontSize = 14;
|
||||||
# BEMENU lines (wayland DMENU):
|
height = 26;
|
||||||
# - camera is 9th entry
|
persistWorkspaces = [ "1" "2" "3" "4" "5" ];
|
||||||
# - flashlight is 10th entry
|
modules.media = false;
|
||||||
# - config is 14th entry. inside that:
|
modules.network = false;
|
||||||
# - autorotate is 11th entry
|
modules.perf = false;
|
||||||
# - system menu is 19th entry
|
modules.windowTitle = false;
|
||||||
# - close is 20th entry
|
# TODO: show modem state
|
||||||
# - power is 15th entry
|
|
||||||
# - close is 16th entry
|
|
||||||
SXMO_BEMENU_LANDSCAPE_LINES = "11"; # default 8
|
|
||||||
SXMO_BEMENU_PORTRAIT_LINES = "16"; # default 16
|
|
||||||
SXMO_LOCK_IDLE_TIME = "15"; # how long between screenoff -> lock -> back to screenoff (default: 8)
|
|
||||||
# gravity: how far to tilt the device before the screen rotates
|
|
||||||
# for a given setting, normal <-> invert requires more movement then left <-> right
|
|
||||||
# i.e. the settingd doesn't feel completely symmetric
|
|
||||||
# SXMO_ROTATION_GRAVITY default is 16374
|
|
||||||
# SXMO_ROTATION_GRAVITY = "12800"; # uncomfortably high
|
|
||||||
# SXMO_ROTATION_GRAVITY = "12500"; # kinda uncomfortable when walking
|
|
||||||
SXMO_ROTATION_GRAVITY = "12000";
|
|
||||||
SXMO_SCREENSHOT_DIR = "/home/colin/Pictures"; # default: "$HOME"
|
|
||||||
|
|
||||||
# sway/wayland scaling:
|
|
||||||
# - conflicting info out there on how scaling actually works
|
|
||||||
# at the least, for things where it matters (mpv), it seems like scale settings have 0 effect on perf
|
|
||||||
# ways to enforce scaling:
|
|
||||||
# - <https://wiki.archlinux.org/title/HiDPI>
|
|
||||||
# - `swaymsg -- output DSI-1 scale 2.0` (scales everything)
|
|
||||||
# - `dconf write /org/gnome/desktop/interface/text-scaling-factor 2.0` (scales ONLY TEXT)
|
|
||||||
# - `GDK_DPI_SCALE=2.0` (scales ONLY TEXT)
|
|
||||||
#
|
|
||||||
# application notes:
|
|
||||||
# - cozy: in landscape, playback position is not visible unless scale <= 1.7
|
|
||||||
# - if in a tab, then scale 1.6 is the max
|
|
||||||
# SXMO_SWAY_SCALE = "1.5"; # hard to press gPodder icons
|
|
||||||
SXMO_SWAY_SCALE = "1.6";
|
|
||||||
# SXMO_SWAY_SCALE = "1.8";
|
|
||||||
# SXMO_SWAY_SCALE = "2";
|
|
||||||
SXMO_WORKSPACE_WRAPPING = "5"; # how many workspaces. default: 4
|
|
||||||
|
|
||||||
# wvkbd layers:
|
|
||||||
# - full
|
|
||||||
# - landscape
|
|
||||||
# - special (e.g. coding symbols like ~)
|
|
||||||
# - emoji
|
|
||||||
# - nav
|
|
||||||
# - simple (like landscape, but no parens/tab/etc; even fewer chars)
|
|
||||||
# - simplegrid (simple, but grid layout)
|
|
||||||
# - dialer (digits)
|
|
||||||
# - cyrillic
|
|
||||||
# - arabic
|
|
||||||
# - persian
|
|
||||||
# - greek
|
|
||||||
# - georgian
|
|
||||||
WVKBD_LANDSCAPE_LAYERS = "landscape,special,emoji";
|
|
||||||
WVKBD_LAYERS = "full,special,emoji";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
boot.loader.efi.canTouchEfiVariables = false;
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||||
sane.persist.enable = false;
|
sane.persist.enable = false; # what we mean here is that the image is immutable; `/` is still tmpfs.
|
||||||
sane.nixcache.enable = false; # don't want to be calling out to dead machines that we're *trying* to rescue
|
sane.nixcache.enable = false; # don't want to be calling out to dead machines that we're *trying* to rescue
|
||||||
|
|
||||||
# auto-login at shell
|
# auto-login at shell
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
# - 1. identify disk IDs: `ls -l /dev/disk/by-id`
|
# - 1. identify disk IDs: `ls -l /dev/disk/by-id`
|
||||||
# - 2. pool these disks: `zpool create -f -m legacy pool raidz ata-ST4000VN008-2DR166_WDH0VB45 ata-ST4000VN008-2DR166_WDH17616 ata-ST4000VN008-2DR166_WDH0VC8Q ata-ST4000VN008-2DR166_WDH17680`
|
# - 2. pool these disks: `zpool create -f -m legacy pool raidz ata-ST4000VN008-2DR166_WDH0VB45 ata-ST4000VN008-2DR166_WDH17616 ata-ST4000VN008-2DR166_WDH0VC8Q ata-ST4000VN008-2DR166_WDH17680`
|
||||||
# - legacy documented: <https://superuser.com/questions/790036/what-is-a-zfs-legacy-mount-point>
|
# - legacy documented: <https://superuser.com/questions/790036/what-is-a-zfs-legacy-mount-point>
|
||||||
|
# - 3. enable acl support: `zfs set acltype=posixacl pool`
|
||||||
#
|
#
|
||||||
# import pools: `zpool import pool`
|
# import pools: `zpool import pool`
|
||||||
# show zfs datasets: `zfs list` (will be empty if haven't imported)
|
# show zfs datasets: `zfs list` (will be empty if haven't imported)
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
# scrub all zfs pools weekly:
|
# scrub all zfs pools weekly:
|
||||||
services.zfs.autoScrub.enable = true;
|
services.zfs.autoScrub.enable = true;
|
||||||
boot.extraModprobeConfig = ''
|
boot.extraModprobeConfig = ''
|
||||||
|
### zfs_arc_max tunable:
|
||||||
# ZFS likes to use half the ram for its own cache and let the kernel push everything else to swap.
|
# ZFS likes to use half the ram for its own cache and let the kernel push everything else to swap.
|
||||||
# so, reduce its cache size
|
# so, reduce its cache size
|
||||||
# see: <https://askubuntu.com/a/1290387>
|
# see: <https://askubuntu.com/a/1290387>
|
||||||
@@ -33,7 +35,13 @@
|
|||||||
# for all tunables, see: `man 4 zfs`
|
# for all tunables, see: `man 4 zfs`
|
||||||
# to update these parameters without rebooting:
|
# to update these parameters without rebooting:
|
||||||
# - `echo '4294967296' | sane-sudo-redirect /sys/module/zfs/parameters/zfs_arc_max`
|
# - `echo '4294967296' | sane-sudo-redirect /sys/module/zfs/parameters/zfs_arc_max`
|
||||||
options zfs zfs_arc_max=4294967296
|
### zfs_bclone_enabled tunable
|
||||||
|
# this allows `cp --reflink=always FOO BAR` to work. i.e. shallow copies.
|
||||||
|
# it's unstable as of 2.2.3. led to *actual* corruption in 2.2.1, but hopefully better by now.
|
||||||
|
# - <https://github.com/openzfs/zfs/issues/405>
|
||||||
|
# note that `du -h` won't *always* show the reduced size for reflink'd files (?).
|
||||||
|
# `zpool get all | grep clone` seems to be the way to *actually* see how much data is being deduped
|
||||||
|
options zfs zfs_arc_max=4294967296 zfs_bclone_enabled=1
|
||||||
'';
|
'';
|
||||||
# to be able to mount the pool like this, make sure to tell zfs to NOT manage it itself.
|
# to be able to mount the pool like this, make sure to tell zfs to NOT manage it itself.
|
||||||
# otherwise local-fs.target will FAIL and you will be dropped into a rescue shell.
|
# otherwise local-fs.target will FAIL and you will be dropped into a rescue shell.
|
||||||
@@ -43,6 +51,7 @@
|
|||||||
fileSystems."/mnt/pool" = {
|
fileSystems."/mnt/pool" = {
|
||||||
device = "pool";
|
device = "pool";
|
||||||
fsType = "zfs";
|
fsType = "zfs";
|
||||||
|
options = [ "acl" ]; #< not sure if this `acl` flag is actually necessary. it mounts without it.
|
||||||
};
|
};
|
||||||
# services.zfs.zed = ... # TODO: zfs can send me emails when disks fail
|
# services.zfs.zed = ... # TODO: zfs can send me emails when disks fail
|
||||||
sane.programs.sysadminUtils.suggestedPrograms = [ "zfs" ];
|
sane.programs.sysadminUtils.suggestedPrograms = [ "zfs" ];
|
||||||
@@ -82,61 +91,45 @@
|
|||||||
};
|
};
|
||||||
sane.fs."/mnt/usb-hdd".mount = {};
|
sane.fs."/mnt/usb-hdd".mount = {};
|
||||||
|
|
||||||
sane.persist.sys.byStore.plaintext = [
|
# FIRST TIME SETUP FOR MEDIA DIRECTORY:
|
||||||
# TODO: this is overly broad; only need media and share directories to be persisted
|
# - set the group stick bit: `sudo find /var/media -type d -exec chmod g+s {} +`
|
||||||
{ user = "colin"; group = "users"; path = "/var/lib/uninsane"; method = "bind"; }
|
# - this ensures new files/dirs inherit the group of their parent dir (instead of the user who creates them)
|
||||||
];
|
# - ensure everything under /var/media is mounted with `-o acl`, to support acls
|
||||||
# force some problematic directories to always get correct permissions:
|
# - ensure all files are rwx by group: `setfacl --recursive --modify d:g::rwx /var/media`
|
||||||
sane.fs."/var/lib/uninsane/media".dir.acl = {
|
# - alternatively, `d:g:media:rwx` to grant `media` group even when file has a different owner, but that's a bit complex
|
||||||
user = "colin"; group = "media"; mode = "0775";
|
sane.persist.sys.byStore.ext = [{
|
||||||
};
|
path = "/var/media";
|
||||||
sane.fs."/var/lib/uninsane/media/archive".dir = {};
|
user = "colin";
|
||||||
|
group = "media";
|
||||||
|
mode = "0775";
|
||||||
|
}];
|
||||||
|
sane.fs."/var/media/archive".dir = {};
|
||||||
# this is file.text instead of symlink.text so that it may be read over a remote mount (where consumers might not have any /nix/store/.../README.md path)
|
# this is file.text instead of symlink.text so that it may be read over a remote mount (where consumers might not have any /nix/store/.../README.md path)
|
||||||
sane.fs."/var/lib/uninsane/media/archive/README.md".file.text = ''
|
sane.fs."/var/media/archive/README.md".file.text = ''
|
||||||
this directory is for media i wish to remove from my library,
|
this directory is for media i wish to remove from my library,
|
||||||
but keep for a short time in case i reverse my decision.
|
but keep for a short time in case i reverse my decision.
|
||||||
treat it like a system trash can.
|
treat it like a system trash can.
|
||||||
'';
|
'';
|
||||||
sane.fs."/var/lib/uninsane/media/Books".dir = {};
|
sane.fs."/var/media/Books".dir = {};
|
||||||
sane.fs."/var/lib/uninsane/media/Books/Audiobooks".dir = {};
|
sane.fs."/var/media/Books/Audiobooks".dir = {};
|
||||||
sane.fs."/var/lib/uninsane/media/Books/Books".dir = {};
|
sane.fs."/var/media/Books/Books".dir = {};
|
||||||
sane.fs."/var/lib/uninsane/media/Books/Visual".dir = {};
|
sane.fs."/var/media/Books/Visual".dir = {};
|
||||||
sane.fs."/var/lib/uninsane/media/collections".dir = {};
|
sane.fs."/var/media/collections".dir = {};
|
||||||
sane.fs."/var/lib/uninsane/media/datasets".dir = {};
|
# sane.fs."/var/media/datasets".dir = {};
|
||||||
sane.fs."/var/lib/uninsane/media/freeleech".dir = {};
|
sane.fs."/var/media/freeleech".dir = {};
|
||||||
sane.fs."/var/lib/uninsane/media/Music".dir = {};
|
sane.fs."/var/media/Music".dir = {};
|
||||||
sane.fs."/var/lib/uninsane/media/Pictures".dir = {};
|
sane.fs."/var/media/Pictures".dir = {};
|
||||||
sane.fs."/var/lib/uninsane/media/Videos".dir = {};
|
sane.fs."/var/media/Videos".dir = {};
|
||||||
sane.fs."/var/lib/uninsane/media/Videos/Film".dir = {};
|
sane.fs."/var/media/Videos/Film".dir = {};
|
||||||
sane.fs."/var/lib/uninsane/media/Videos/Shows".dir = {};
|
sane.fs."/var/media/Videos/Shows".dir = {};
|
||||||
sane.fs."/var/lib/uninsane/media/Videos/Talks".dir = {};
|
sane.fs."/var/media/Videos/Talks".dir = {};
|
||||||
|
|
||||||
# this is file.text instead of symlink.text so that it may be read over a remote mount (where consumers might not have any /nix/store/.../README.md path)
|
# this is file.text instead of symlink.text so that it may be read over a remote mount (where consumers might not have any /nix/store/.../README.md path)
|
||||||
sane.fs."/var/lib/uninsane/datasets/README.md".file.text = ''
|
sane.fs."/var/lib/uninsane/datasets/README.md".file.text = ''
|
||||||
this directory may seem redundant with ../media/datasets. it isn't.
|
this directory may seem redundant with ../media/datasets. it isn't.
|
||||||
this directory exists on SSD, allowing for speedy access to specific datasets when necessary.
|
this directory exists on SSD, allowing for speedy access to specific datasets when necessary.
|
||||||
the contents should be a subset of what's in ../media/datasets.
|
the contents should be a subset of what's in ../media/datasets.
|
||||||
'';
|
'';
|
||||||
# make sure large media is stored to the HDD
|
|
||||||
sane.persist.sys.byStore.ext = [
|
|
||||||
{
|
|
||||||
user = "colin";
|
|
||||||
group = "users";
|
|
||||||
mode = "0777";
|
|
||||||
path = "/var/lib/uninsane/media/Videos";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
user = "colin";
|
|
||||||
group = "users";
|
|
||||||
mode = "0777";
|
|
||||||
path = "/var/lib/uninsane/media/freeleech";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
user = "colin";
|
|
||||||
group = "users";
|
|
||||||
mode = "0777";
|
|
||||||
path = "/var/lib/uninsane/media/datasets";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
# btrfs doesn't easily support swapfiles
|
# btrfs doesn't easily support swapfiles
|
||||||
# swapDevices = [
|
# swapDevices = [
|
||||||
|
@@ -87,7 +87,7 @@ in
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
preSetup = ''
|
preSetup = ''
|
||||||
${ip} netns add ovpns || echo "ovpns already exists"
|
${ip} netns add ovpns || (test -e /run/netns/ovpns && echo "ovpns already exists")
|
||||||
'';
|
'';
|
||||||
postShutdown = ''
|
postShutdown = ''
|
||||||
${in-ns} ip link del ovpns-veth-b || echo "couldn't delete ovpns-veth-b"
|
${in-ns} ip link del ovpns-veth-b || echo "couldn't delete ovpns-veth-b"
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
fileSystems."/var/export/media" = {
|
fileSystems."/var/export/media" = {
|
||||||
# everything in here could be considered publicly readable (based on the viewer's legal jurisdiction)
|
# everything in here could be considered publicly readable (based on the viewer's legal jurisdiction)
|
||||||
device = "/var/lib/uninsane/media";
|
device = "/var/media";
|
||||||
options = [ "rbind" ];
|
options = [ "rbind" ];
|
||||||
};
|
};
|
||||||
# fileSystems."/var/export/playground" = {
|
# fileSystems."/var/export/playground" = {
|
||||||
|
@@ -26,7 +26,7 @@
|
|||||||
description = "NFS server portmapper";
|
description = "NFS server portmapper";
|
||||||
};
|
};
|
||||||
sane.ports.ports."2049" = {
|
sane.ports.ports."2049" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" "udp" ];
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
description = "NFS server";
|
description = "NFS server";
|
||||||
};
|
};
|
||||||
@@ -51,6 +51,23 @@
|
|||||||
services.nfs.server.mountdPort = 4002;
|
services.nfs.server.mountdPort = 4002;
|
||||||
services.nfs.server.statdPort = 4000;
|
services.nfs.server.statdPort = 4000;
|
||||||
|
|
||||||
|
services.nfs.extraConfig = ''
|
||||||
|
[nfsd]
|
||||||
|
# XXX: NFS over UDP REQUIRES SPECIAL CONFIG TO AVOID DATA LOSS.
|
||||||
|
# see `man 5 nfs`: "Using NFS over UDP on high-speed links".
|
||||||
|
# it's actually just a general property of UDP over IPv4 (IPv6 fixes it).
|
||||||
|
# both the client and the server should configure a shorter-than-default IPv4 fragment reassembly window to mitigate.
|
||||||
|
# OTOH, tunneling NFS over Wireguard also bypasses this weakness, because a mis-assembled packet would not have a valid signature.
|
||||||
|
udp=y
|
||||||
|
|
||||||
|
[exports]
|
||||||
|
# all export paths are relative to rootdir.
|
||||||
|
# for NFSv4, the export with fsid=0 behaves as `/` publicly,
|
||||||
|
# but NFSv3 implements no such feature.
|
||||||
|
# using `rootdir` instead of relying on `fsid=0` allows consistent export paths regardless of NFS proto version
|
||||||
|
rootdir=/var/export
|
||||||
|
'';
|
||||||
|
|
||||||
# format:
|
# format:
|
||||||
# fspoint visibility(options)
|
# fspoint visibility(options)
|
||||||
# options:
|
# options:
|
||||||
@@ -85,13 +102,20 @@
|
|||||||
in "${export} 10.78.79.0/22(${lib.concatStringsSep "," lanOpts}) 10.0.10.0/24(${lib.concatStringsSep "," vpnOpts})";
|
in "${export} 10.78.79.0/22(${lib.concatStringsSep "," lanOpts}) 10.0.10.0/24(${lib.concatStringsSep "," vpnOpts})";
|
||||||
in lib.concatStringsSep "\n" [
|
in lib.concatStringsSep "\n" [
|
||||||
(fmtExport {
|
(fmtExport {
|
||||||
export = "/var/export";
|
export = "/";
|
||||||
baseOpts = [ "crossmnt" "fsid=root" ];
|
baseOpts = [ "crossmnt" "fsid=root" ];
|
||||||
extraLanOpts = [ "ro" ];
|
extraLanOpts = [ "ro" ];
|
||||||
extraVpnOpts = [ "rw" "no_root_squash" ];
|
extraVpnOpts = [ "rw" "no_root_squash" ];
|
||||||
})
|
})
|
||||||
(fmtExport {
|
(fmtExport {
|
||||||
export = "/var/export/playground";
|
# provide /media as an explicit export. NFSv4 can transparently mount a subdir of an export, but NFSv3 can only mount paths which are exports.
|
||||||
|
export = "/media";
|
||||||
|
baseOpts = [ "crossmnt" ]; # TODO: is crossmnt needed here?
|
||||||
|
extraLanOpts = [ "ro" ];
|
||||||
|
extraVpnOpts = [ "rw" "no_root_squash" ];
|
||||||
|
})
|
||||||
|
(fmtExport {
|
||||||
|
export = "/playground";
|
||||||
baseOpts = [
|
baseOpts = [
|
||||||
"mountpoint"
|
"mountpoint"
|
||||||
"all_squash"
|
"all_squash"
|
||||||
|
@@ -6,107 +6,25 @@
|
|||||||
# - nixos example: <repo:nixos/nixpkgs:nixos/tests/sftpgo.nix>
|
# - nixos example: <repo:nixos/nixpkgs:nixos/tests/sftpgo.nix>
|
||||||
#
|
#
|
||||||
# sftpgo is a FTP server that also supports WebDAV, SFTP, and web clients.
|
# sftpgo is a FTP server that also supports WebDAV, SFTP, and web clients.
|
||||||
#
|
|
||||||
# TODO: change umask so sftpgo-created files default to 644.
|
|
||||||
# - it does indeed appear that the 600 is not something sftpgo is explicitly doing.
|
|
||||||
|
|
||||||
|
|
||||||
{ config, lib, pkgs, sane-lib, ... }:
|
{ config, lib, pkgs, sane-lib, ... }:
|
||||||
let
|
let
|
||||||
# user permissions:
|
sftpgo_external_auth_hook = pkgs.static-nix-shell.mkPython3Bin {
|
||||||
# - see <repo:drakkan/sftpgo:internal/dataprovider/user.go>
|
|
||||||
# - "*" = grant all permissions
|
|
||||||
# - read-only perms:
|
|
||||||
# - "list" = list files and directories
|
|
||||||
# - "download"
|
|
||||||
# - rw perms:
|
|
||||||
# - "upload"
|
|
||||||
# - "overwrite" = allow uploads to replace existing files
|
|
||||||
# - "delete" = delete files and directories
|
|
||||||
# - "delete_files"
|
|
||||||
# - "delete_dirs"
|
|
||||||
# - "rename" = rename files and directories
|
|
||||||
# - "rename_files"
|
|
||||||
# - "rename_dirs"
|
|
||||||
# - "create_dirs"
|
|
||||||
# - "create_symlinks"
|
|
||||||
# - "chmod"
|
|
||||||
# - "chown"
|
|
||||||
# - "chtimes" = change atime/mtime (access and modification times)
|
|
||||||
#
|
|
||||||
# home_dir:
|
|
||||||
# - it seems (empirically) that a user can't cd above their home directory.
|
|
||||||
# though i don't have a reference for that in the docs.
|
|
||||||
authResponseSuccess = {
|
|
||||||
status = 1;
|
|
||||||
username = "anonymous";
|
|
||||||
expiration_date = 0;
|
|
||||||
home_dir = "/var/export";
|
|
||||||
# uid/gid 0 means to inherit sftpgo uid.
|
|
||||||
# - i.e. users can't read files which Linux user `sftpgo` can't read
|
|
||||||
# - uploaded files belong to Linux user `sftpgo`
|
|
||||||
# other uid/gid values aren't possible for localfs backend, unless i let sftpgo use `sudo`.
|
|
||||||
uid = 0;
|
|
||||||
gid = 0;
|
|
||||||
# uid = 65534;
|
|
||||||
# gid = 65534;
|
|
||||||
max_sessions = 0;
|
|
||||||
# quota_*: 0 means to not use SFTP's quota system
|
|
||||||
quota_size = 0;
|
|
||||||
quota_files = 0;
|
|
||||||
permissions = {
|
|
||||||
"/" = [ "list" "download" ];
|
|
||||||
"/playground" = [
|
|
||||||
# read-only:
|
|
||||||
"list"
|
|
||||||
"download"
|
|
||||||
# write:
|
|
||||||
"upload"
|
|
||||||
"overwrite"
|
|
||||||
"delete"
|
|
||||||
"rename"
|
|
||||||
"create_dirs"
|
|
||||||
"create_symlinks"
|
|
||||||
# intentionally omitted:
|
|
||||||
# "chmod"
|
|
||||||
# "chown"
|
|
||||||
# "chtimes"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
upload_bandwidth = 0;
|
|
||||||
download_bandwidth = 0;
|
|
||||||
filters = {
|
|
||||||
allowed_ip = [];
|
|
||||||
denied_ip = [];
|
|
||||||
};
|
|
||||||
public_keys = [];
|
|
||||||
# other fields:
|
|
||||||
# ? groups
|
|
||||||
# ? virtual_folders
|
|
||||||
};
|
|
||||||
authResponseFail = {
|
|
||||||
username = "";
|
|
||||||
};
|
|
||||||
authSuccessJson = pkgs.writeText "sftp-auth-success.json" (builtins.toJSON authResponseSuccess);
|
|
||||||
authFailJson = pkgs.writeText "sftp-auth-fail.json" (builtins.toJSON authResponseFail);
|
|
||||||
unwrappedAuthProgram = pkgs.static-nix-shell.mkBash {
|
|
||||||
pname = "sftpgo_external_auth_hook";
|
pname = "sftpgo_external_auth_hook";
|
||||||
srcRoot = ./.;
|
srcRoot = ./.;
|
||||||
pkgs = [ "coreutils" ];
|
|
||||||
};
|
};
|
||||||
authProgram = pkgs.writeShellScript "sftpgo-auth-hook" ''
|
|
||||||
${unwrappedAuthProgram}/bin/sftpgo_external_auth_hook ${authFailJson} ${authSuccessJson}
|
|
||||||
'';
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# Client initiates a FTP "control connection" on port 21.
|
# Client initiates a FTP "control connection" on port 21.
|
||||||
# - this handles the client -> server commands, and the server -> client status, but not the actual data
|
# - this handles the client -> server commands, and the server -> client status, but not the actual data
|
||||||
# - file data, directory listings, etc need to be transferred on an ephemeral "data port".
|
# - file data, directory listings, etc need to be transferred on an ephemeral "data port".
|
||||||
# - 50000-50100 is a common port range for this.
|
# - 50000-50100 is a common port range for this.
|
||||||
|
# 50000 is used by soulseek.
|
||||||
sane.ports.ports = {
|
sane.ports.ports = {
|
||||||
"21" = {
|
"21" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-FTP server";
|
description = "colin-FTP server";
|
||||||
};
|
};
|
||||||
} // (sane-lib.mapToAttrs
|
} // (sane-lib.mapToAttrs
|
||||||
@@ -115,10 +33,11 @@ in
|
|||||||
value = {
|
value = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
description = "colin-FTP server data port range";
|
description = "colin-FTP server data port range";
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
(lib.range 50000 50100)
|
(lib.range 50050 50100)
|
||||||
);
|
);
|
||||||
|
|
||||||
services.sftpgo = {
|
services.sftpgo = {
|
||||||
@@ -134,7 +53,7 @@ in
|
|||||||
debug = true;
|
debug = true;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
# binding this means any LAN client can connect
|
# binding this means any LAN client can connect (also WAN traffic forwarded from the gateway)
|
||||||
address = "10.78.79.51";
|
address = "10.78.79.51";
|
||||||
port = 21;
|
port = 21;
|
||||||
debug = true;
|
debug = true;
|
||||||
@@ -145,22 +64,25 @@ in
|
|||||||
disable_active_mode = true;
|
disable_active_mode = true;
|
||||||
hash_support = true;
|
hash_support = true;
|
||||||
passive_port_range = {
|
passive_port_range = {
|
||||||
start = 50000;
|
start = 50050;
|
||||||
end = 50100;
|
end = 50100;
|
||||||
};
|
};
|
||||||
|
|
||||||
banner = ''
|
banner = ''
|
||||||
Welcome, friends, to Colin's read-only FTP server! Also available via NFS on the same host.
|
Welcome, friends, to Colin's FTP server! Also available via NFS on the same host, but LAN-only.
|
||||||
|
|
||||||
|
Read-only access (LAN-restricted):
|
||||||
Username: "anonymous"
|
Username: "anonymous"
|
||||||
Password: "anonymous"
|
Password: "anonymous"
|
||||||
CONFIGURE YOUR CLIENT FOR "PASSIVE" mode, e.g. `ftp --passive uninsane.org`
|
|
||||||
Please let me know if anything's broken or not as it should be. Otherwise, browse and DL freely :)
|
CONFIGURE YOUR CLIENT FOR "PASSIVE" mode, e.g. `ftp --passive uninsane.org`.
|
||||||
|
Please let me know if anything's broken or not as it should be. Otherwise, browse and transfer freely :)
|
||||||
'';
|
'';
|
||||||
|
|
||||||
};
|
};
|
||||||
data_provider = {
|
data_provider = {
|
||||||
driver = "memory";
|
driver = "memory";
|
||||||
external_auth_hook = "${authProgram}";
|
external_auth_hook = "${sftpgo_external_auth_hook}/bin/sftpgo_external_auth_hook";
|
||||||
# track_quota:
|
# track_quota:
|
||||||
# - 0: disable quota tracking
|
# - 0: disable quota tracking
|
||||||
# - 1: quota is updated on every upload/delete, even if user has no quota restriction
|
# - 1: quota is updated on every upload/delete, even if user has no quota restriction
|
||||||
@@ -170,17 +92,20 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
users.users.sftpgo.extraGroups = [ "export" ];
|
users.users.sftpgo.extraGroups = [
|
||||||
|
"export"
|
||||||
|
"media"
|
||||||
|
];
|
||||||
|
|
||||||
systemd.services.sftpgo = {
|
systemd.services.sftpgo = {
|
||||||
after = [ "network-online.target" ];
|
after = [ "network-online.target" ];
|
||||||
wants = [ "network-online.target" ];
|
wants = [ "network-online.target" ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
ReadOnlyPaths = [ "/var/export" ];
|
ReadWritePaths = [ "/var/export" ];
|
||||||
ReadWritePaths = [ "/var/export/playground" ];
|
|
||||||
|
|
||||||
Restart = "always";
|
Restart = "always";
|
||||||
RestartSec = "20s";
|
RestartSec = "20s";
|
||||||
|
UMask = lib.mkForce "0002";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env nix-shell
|
#!/usr/bin/env nix-shell
|
||||||
#!nix-shell -i bash -p coreutils
|
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])"
|
||||||
# vim: set filetype=bash :
|
# vim: set filetype=python :
|
||||||
#
|
#
|
||||||
# available environment variables:
|
# available environment variables:
|
||||||
# - SFTPGO_AUTHD_USERNAME
|
# - SFTPGO_AUTHD_USERNAME
|
||||||
@@ -12,12 +12,146 @@
|
|||||||
# - SFTPGO_AUTHD_KEYBOARD_INTERACTIVE
|
# - SFTPGO_AUTHD_KEYBOARD_INTERACTIVE
|
||||||
# - SFTPGO_AUTHD_TLS_CERT
|
# - SFTPGO_AUTHD_TLS_CERT
|
||||||
#
|
#
|
||||||
|
# user permissions:
|
||||||
|
# - see <repo:drakkan/sftpgo:internal/dataprovider/user.go>
|
||||||
|
# - "*" = grant all permissions
|
||||||
|
# - read-only perms:
|
||||||
|
# - "list" = list files and directories
|
||||||
|
# - "download"
|
||||||
|
# - rw perms:
|
||||||
|
# - "upload"
|
||||||
|
# - "overwrite" = allow uploads to replace existing files
|
||||||
|
# - "delete" = delete files and directories
|
||||||
|
# - "delete_files"
|
||||||
|
# - "delete_dirs"
|
||||||
|
# - "rename" = rename files and directories
|
||||||
|
# - "rename_files"
|
||||||
|
# - "rename_dirs"
|
||||||
|
# - "create_dirs"
|
||||||
|
# - "create_symlinks"
|
||||||
|
# - "chmod"
|
||||||
|
# - "chown"
|
||||||
|
# - "chtimes" = change atime/mtime (access and modification times)
|
||||||
#
|
#
|
||||||
# call with <script_name> /path/to/fail/response.json /path/to/success/response.json
|
# home_dir:
|
||||||
|
# - it seems (empirically) that a user can't cd above their home directory.
|
||||||
|
# though i don't have a reference for that in the docs.
|
||||||
|
|
||||||
|
import crypt
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
if [ "$SFTPGO_AUTHD_USERNAME" = "anonymous" ]; then
|
from hmac import compare_digest
|
||||||
cat "$2"
|
|
||||||
else
|
authFail = dict(username="")
|
||||||
cat "$1"
|
|
||||||
fi
|
PERM_RO = [ "list", "download" ]
|
||||||
|
PERM_RW = [
|
||||||
|
# read-only:
|
||||||
|
"list",
|
||||||
|
"download",
|
||||||
|
# write:
|
||||||
|
"upload",
|
||||||
|
"overwrite",
|
||||||
|
"delete",
|
||||||
|
"rename",
|
||||||
|
"create_dirs",
|
||||||
|
"create_symlinks",
|
||||||
|
# intentionally omitted:
|
||||||
|
# "chmod",
|
||||||
|
# "chown",
|
||||||
|
# "chtimes",
|
||||||
|
]
|
||||||
|
|
||||||
|
TRUSTED_CREDS = [
|
||||||
|
# /etc/shadow style creds.
|
||||||
|
# mkpasswd -m sha-512
|
||||||
|
# $<method>$<salt>$<hash>
|
||||||
|
"$6$Zq3c2u4ghUH4S6EP$pOuRt13sEKfX31OqPbbd1LuhS21C9MICMc94iRdTAgdAcJ9h95gQH/6Jf6Ie4Obb0oxQtojRJ1Pd/9QHOlFMW." #< m. rocket boy
|
||||||
|
]
|
||||||
|
|
||||||
|
def mkAuthOk(username: str, permissions: dict[str, list[str]]) -> dict:
|
||||||
|
return dict(
|
||||||
|
status = 1,
|
||||||
|
username = username,
|
||||||
|
expiration_date = 0,
|
||||||
|
home_dir = "/var/export",
|
||||||
|
# uid/gid 0 means to inherit sftpgo uid.
|
||||||
|
# - i.e. users can't read files which Linux user `sftpgo` can't read
|
||||||
|
# - uploaded files belong to Linux user `sftpgo`
|
||||||
|
# other uid/gid values aren't possible for localfs backend, unless i let sftpgo use `sudo`.
|
||||||
|
uid = 0,
|
||||||
|
gid = 0,
|
||||||
|
# uid = 65534,
|
||||||
|
# gid = 65534,
|
||||||
|
max_sessions = 0,
|
||||||
|
# quota_*: 0 means to not use SFTP's quota system
|
||||||
|
quota_size = 0,
|
||||||
|
quota_files = 0,
|
||||||
|
permissions = permissions,
|
||||||
|
upload_bandwidth = 0,
|
||||||
|
download_bandwidth = 0,
|
||||||
|
filters = dict(
|
||||||
|
allowed_ip = [],
|
||||||
|
denied_ip = [],
|
||||||
|
),
|
||||||
|
public_keys = [],
|
||||||
|
# other fields:
|
||||||
|
# ? groups
|
||||||
|
# ? virtual_folders
|
||||||
|
)
|
||||||
|
|
||||||
|
def isLan(ip: str) -> bool:
|
||||||
|
return ip.startswith("10.78.76.") \
|
||||||
|
or ip.startswith("10.78.77.") \
|
||||||
|
or ip.startswith("10.78.78.") \
|
||||||
|
or ip.startswith("10.78.79.")
|
||||||
|
|
||||||
|
def isWireguard(ip: str) -> bool:
|
||||||
|
return ip.startswith("10.0.10.")
|
||||||
|
|
||||||
|
def isTrustedCred(password: str) -> bool:
|
||||||
|
for cred in TRUSTED_CREDS:
|
||||||
|
_, method, salt, hash_ = cred.split("$")
|
||||||
|
# assert method == "6", f"unrecognized crypt entry: {cred}"
|
||||||
|
if crypt.crypt(password, f"${method}${salt}") == cred:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def getAuthResponse(ip: str, username: str, password: str) -> dict:
|
||||||
|
"""
|
||||||
|
return a sftpgo auth response either denying the user or approving them
|
||||||
|
with a set of permissions.
|
||||||
|
"""
|
||||||
|
if isTrustedCred(password) and username != "colin":
|
||||||
|
# allow r/w access from those with a special token
|
||||||
|
return mkAuthOk(username, permissions = {
|
||||||
|
"/": PERM_RW,
|
||||||
|
"/playground": PERM_RW,
|
||||||
|
})
|
||||||
|
if isWireguard(ip):
|
||||||
|
# allow any user from wireguard
|
||||||
|
return mkAuthOk(username, permissions = {
|
||||||
|
"/": PERM_RW,
|
||||||
|
"/playground": PERM_RW,
|
||||||
|
})
|
||||||
|
if isLan(ip):
|
||||||
|
if username == "anonymous":
|
||||||
|
# allow anonymous users on the LAN
|
||||||
|
return mkAuthOk("anonymous", permissions = {
|
||||||
|
"/": PERM_RO,
|
||||||
|
"/playground": PERM_RW,
|
||||||
|
})
|
||||||
|
|
||||||
|
return authFail
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ip = os.environ.get("SFTPGO_AUTHD_IP", "")
|
||||||
|
username = os.environ.get("SFTPGO_AUTHD_USERNAME", "")
|
||||||
|
password = os.environ.get("SFTPGO_AUTHD_PASSWORD", "")
|
||||||
|
resp = getAuthResponse(ip, username, password)
|
||||||
|
print(json.dumps(resp))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
@@ -10,16 +10,21 @@ let
|
|||||||
uiPort = 1234; # default ui port is 1234
|
uiPort = 1234; # default ui port is 1234
|
||||||
backendPort = 8536; # default backend port is 8536
|
backendPort = 8536; # default backend port is 8536
|
||||||
#^ i guess the "backend" port is used for federation?
|
#^ i guess the "backend" port is used for federation?
|
||||||
pict-rs = pkgs.pict-rs.overrideAttrs (upstream: {
|
pict-rs = pkgs.pict-rs;
|
||||||
# as of v 0.4.2, all non-GIF video is forcibly transcoded.
|
# pict-rs = pkgs.pict-rs.overrideAttrs (upstream: {
|
||||||
# that breaks lemmy, because of the request latency.
|
# # as of v0.4.2, all non-GIF video is forcibly transcoded.
|
||||||
# and it eats up hella CPU.
|
# # that breaks lemmy, because of the request latency.
|
||||||
# pict-rs is iffy around video altogether: mp4 seems the best supported.
|
# # and it eats up hella CPU.
|
||||||
postPatch = (upstream.postPatch or "") + ''
|
# # pict-rs is iffy around video altogether: mp4 seems the best supported.
|
||||||
substituteInPlace src/validate.rs \
|
# # XXX: this patch no longer applies after 0.5.10 -> 0.5.11 update.
|
||||||
--replace 'if transcode_options.needs_reencode() {' 'if false {'
|
# # git log is hard to parse, but *suggests* that video is natively supported
|
||||||
'';
|
# # better than in the 0.4.2 days, e.g. 5fd59fc5b42d31559120dc28bfef4e5002fb509e
|
||||||
});
|
# # "Change commandline flag to allow disabling video, since it is enabled by default"
|
||||||
|
# postPatch = (upstream.postPatch or "") + ''
|
||||||
|
# substituteInPlace src/validate.rs \
|
||||||
|
# --replace 'if transcode_options.needs_reencode() {' 'if false {'
|
||||||
|
# '';
|
||||||
|
# });
|
||||||
in {
|
in {
|
||||||
services.lemmy = {
|
services.lemmy = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
|
|
||||||
|
lib.mkIf false #< i don't actively use navidrome
|
||||||
{
|
{
|
||||||
sane.persist.sys.byStore.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
{ user = "navidrome"; group = "navidrome"; path = "/var/lib/navidrome"; method = "bind"; }
|
{ user = "navidrome"; group = "navidrome"; path = "/var/lib/navidrome"; method = "bind"; }
|
||||||
@@ -9,7 +10,7 @@
|
|||||||
# docs: https://www.navidrome.org/docs/usage/configuration-options/
|
# docs: https://www.navidrome.org/docs/usage/configuration-options/
|
||||||
Address = "127.0.0.1";
|
Address = "127.0.0.1";
|
||||||
Port = 4533;
|
Port = 4533;
|
||||||
MusicFolder = "/var/lib/uninsane/media/Music";
|
MusicFolder = "/var/media/Music";
|
||||||
CovertArtPriority = "*.jpg, *.JPG, *.png, *.PNG, embedded";
|
CovertArtPriority = "*.jpg, *.JPG, *.png, *.PNG, embedded";
|
||||||
AutoImportPlaylists = false;
|
AutoImportPlaylists = false;
|
||||||
ScanSchedule = "@every 1h";
|
ScanSchedule = "@every 1h";
|
||||||
|
@@ -55,8 +55,8 @@ in
|
|||||||
|
|
||||||
# web blog/personal site
|
# web blog/personal site
|
||||||
# alternative way to link stuff into the share:
|
# alternative way to link stuff into the share:
|
||||||
# sane.fs."/var/lib/uninsane/share/Ubunchu".mount.bind = "/var/lib/uninsane/media/Books/Visual/HiroshiSeo/Ubunchu";
|
# sane.fs."/var/www/sites/uninsane.org/share/Ubunchu".mount.bind = "/var/media/Books/Visual/HiroshiSeo/Ubunchu";
|
||||||
# sane.fs."/var/lib/uninsane/media/Books/Visual/HiroshiSeo/Ubunchu".dir = {};
|
# sane.fs."/var/media/Books/Visual/HiroshiSeo/Ubunchu".dir = {};
|
||||||
services.nginx.virtualHosts."uninsane.org" = publog {
|
services.nginx.virtualHosts."uninsane.org" = publog {
|
||||||
# a lot of places hardcode https://uninsane.org,
|
# a lot of places hardcode https://uninsane.org,
|
||||||
# and then when we mix http + non-https, we get CORS violations
|
# and then when we mix http + non-https, we get CORS violations
|
||||||
|
@@ -3,10 +3,14 @@
|
|||||||
#
|
#
|
||||||
# config precedence (higher precedence overrules lower precedence):
|
# config precedence (higher precedence overrules lower precedence):
|
||||||
# - Default Values < Environment Variables < YAML Configuraiton File < Command Line Arguments
|
# - Default Values < Environment Variables < YAML Configuraiton File < Command Line Arguments
|
||||||
|
#
|
||||||
|
# debugging:
|
||||||
|
# - soulseek is just *flaky*. if you see e.g. DNS errors, even though you can't replicate them via `dig` or `getent ahostsv4`, just give it 10 minutes to work out:
|
||||||
|
# - "Soulseek.AddressException: Failed to resolve address 'vps.slsknet.org': Resource temporarily unavailable"
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
{
|
{
|
||||||
sane.persist.sys.byStore.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
{ user = "slskd"; group = "slskd"; path = "/var/lib/slskd"; method = "bind"; }
|
{ user = "slskd"; group = "media"; path = "/var/lib/slskd"; method = "bind"; }
|
||||||
];
|
];
|
||||||
sops.secrets."slskd_env" = {
|
sops.secrets."slskd_env" = {
|
||||||
owner = config.users.users.slskd.name;
|
owner = config.users.users.slskd.name;
|
||||||
@@ -15,7 +19,7 @@
|
|||||||
|
|
||||||
users.users.slskd.extraGroups = [ "media" ];
|
users.users.slskd.extraGroups = [ "media" ];
|
||||||
|
|
||||||
sane.ports.ports."50000" = {
|
sane.ports.ports."50300" = {
|
||||||
protocol = [ "tcp" ];
|
protocol = [ "tcp" ];
|
||||||
# not visible to WAN: i run this in a separate netns
|
# not visible to WAN: i run this in a separate netns
|
||||||
visibleTo.ovpn = true;
|
visibleTo.ovpn = true;
|
||||||
@@ -28,12 +32,14 @@
|
|||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
proxyPass = "http://10.0.1.6:5001";
|
proxyPass = "http://10.0.1.6:5030";
|
||||||
proxyWebsockets = true;
|
proxyWebsockets = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services.slskd.enable = true;
|
services.slskd.enable = true;
|
||||||
|
services.slskd.domain = null; # i'll manage nginx for it
|
||||||
|
services.slskd.group = "media";
|
||||||
# env file, for auth (SLSKD_SLSK_PASSWORD, SLSKD_SLSK_USERNAME)
|
# env file, for auth (SLSKD_SLSK_PASSWORD, SLSKD_SLSK_USERNAME)
|
||||||
services.slskd.environmentFile = config.sops.secrets.slskd_env.path;
|
services.slskd.environmentFile = config.sops.secrets.slskd_env.path;
|
||||||
services.slskd.settings = {
|
services.slskd.settings = {
|
||||||
@@ -44,13 +50,13 @@
|
|||||||
# [Alias]/path/on/disk
|
# [Alias]/path/on/disk
|
||||||
# NOTE: Music library is quick to scan; videos take a solid 10min to scan.
|
# NOTE: Music library is quick to scan; videos take a solid 10min to scan.
|
||||||
# TODO: re-enable the other libraries
|
# TODO: re-enable the other libraries
|
||||||
# "[Audioooks]/var/lib/uninsane/media/Books/Audiobooks"
|
# "[Audioooks]/var/media/Books/Audiobooks"
|
||||||
# "[Books]/var/lib/uninsane/media/Books/Books"
|
# "[Books]/var/media/Books/Books"
|
||||||
# "[Manga]/var/lib/uninsane/media/Books/Visual"
|
# "[Manga]/var/media/Books/Visual"
|
||||||
# "[games]/var/lib/uninsane/media/games"
|
# "[games]/var/media/games"
|
||||||
"[Music]/var/lib/uninsane/media/Music"
|
"[Music]/var/media/Music"
|
||||||
# "[Film]/var/lib/uninsane/media/Videos/Film"
|
# "[Film]/var/media/Videos/Film"
|
||||||
# "[Shows]/var/lib/uninsane/media/Videos/Shows"
|
# "[Shows]/var/media/Videos/Shows"
|
||||||
];
|
];
|
||||||
# directories.downloads = "..." # TODO
|
# directories.downloads = "..." # TODO
|
||||||
# directories.incomplete = "..." # TODO
|
# directories.incomplete = "..." # TODO
|
||||||
@@ -68,7 +74,6 @@
|
|||||||
NetworkNamespacePath = "/run/netns/ovpns";
|
NetworkNamespacePath = "/run/netns/ovpns";
|
||||||
Restart = lib.mkForce "always"; # exits "success" when it fails to connect to soulseek server
|
Restart = lib.mkForce "always"; # exits "success" when it fails to connect to soulseek server
|
||||||
RestartSec = "60s";
|
RestartSec = "60s";
|
||||||
Group = "media";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,41 @@ let
|
|||||||
--replace-fail 'set(TR_USER_AGENT_PREFIX "''${TR_SEMVER}")' 'set(TR_USER_AGENT_PREFIX "3.00")'
|
--replace-fail 'set(TR_USER_AGENT_PREFIX "''${TR_SEMVER}")' 'set(TR_USER_AGENT_PREFIX "3.00")'
|
||||||
'';
|
'';
|
||||||
});
|
});
|
||||||
|
download-dir = "/var/media/torrents";
|
||||||
|
torrent-done = pkgs.writeShellApplication {
|
||||||
|
name = "torrent-done";
|
||||||
|
runtimeInputs = with pkgs; [
|
||||||
|
rsync
|
||||||
|
util-linux
|
||||||
|
];
|
||||||
|
text = ''
|
||||||
|
destructive() {
|
||||||
|
if [ -n "''${TR_DRY_RUN-}" ]; then
|
||||||
|
echo "$*"
|
||||||
|
else
|
||||||
|
"$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
if [[ "$TR_TORRENT_DIR" =~ ^.*freeleech.*$ ]]; then
|
||||||
|
# freeleech torrents have no place in my permanent library
|
||||||
|
echo "freeleech: nothing to do"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if ! [[ "$TR_TORRENT_DIR" =~ ^${download-dir}/.*$ ]]; then
|
||||||
|
echo "unexpected torrent dir, aborting: $TR_TORRENT_DIR"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
REL_DIR="''${TR_TORRENT_DIR#${download-dir}/}"
|
||||||
|
MEDIA_DIR="/var/media/$REL_DIR"
|
||||||
|
|
||||||
|
destructive mkdir -p "$(dirname "$MEDIA_DIR")"
|
||||||
|
destructive rsync -arv "$TR_TORRENT_DIR/" "$MEDIA_DIR/"
|
||||||
|
# dedupe the whole media library.
|
||||||
|
# yeah, a bit excessive: move this to a cron job if that's problematic.
|
||||||
|
destructive hardlink /var/media --reflink=always --ignore-time --verbose
|
||||||
|
'';
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
sane.persist.sys.byStore.plaintext = [
|
sane.persist.sys.byStore.plaintext = [
|
||||||
@@ -72,11 +107,23 @@ in
|
|||||||
# see: https://git.zknt.org/mirror/transmission/commit/cfce6e2e3a9b9d31a9dafedd0bdc8bf2cdb6e876?lang=bg-BG
|
# see: https://git.zknt.org/mirror/transmission/commit/cfce6e2e3a9b9d31a9dafedd0bdc8bf2cdb6e876?lang=bg-BG
|
||||||
anti-brute-force-enabled = false;
|
anti-brute-force-enabled = false;
|
||||||
|
|
||||||
download-dir = "/var/lib/uninsane/media";
|
inherit download-dir;
|
||||||
incomplete-dir = "/var/lib/uninsane/media/incomplete";
|
incomplete-dir = "${download-dir}/incomplete";
|
||||||
# transmission regularly fails to move stuff from the incomplete dir to the main one, so disable:
|
# transmission regularly fails to move stuff from the incomplete dir to the main one, so disable:
|
||||||
# TODO: uncomment this line!
|
|
||||||
incomplete-dir-enabled = false;
|
incomplete-dir-enabled = false;
|
||||||
|
|
||||||
|
# env vars available in script:
|
||||||
|
# - TR_APP_VERSION - Transmission's short version string, e.g. `4.0.0`
|
||||||
|
# - TR_TIME_LOCALTIME
|
||||||
|
# - TR_TORRENT_BYTES_DOWNLOADED - Number of bytes that were downloaded for this torrent
|
||||||
|
# - TR_TORRENT_DIR - Location of the downloaded data
|
||||||
|
# - TR_TORRENT_HASH - The torrent's info hash
|
||||||
|
# - TR_TORRENT_ID
|
||||||
|
# - TR_TORRENT_LABELS - A comma-delimited list of the torrent's labels
|
||||||
|
# - TR_TORRENT_NAME - Name of torrent (not filename)
|
||||||
|
# - TR_TORRENT_TRACKERS - A comma-delimited list of the torrent's trackers' announce URLs
|
||||||
|
script-torrent-done-enabled = true;
|
||||||
|
script-torrent-done-filename = "${torrent-done}/bin/torrent-done";
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.transmission.after = [ "wireguard-wg-ovpns.service" ];
|
systemd.services.transmission.after = [ "wireguard-wg-ovpns.service" ];
|
||||||
@@ -86,6 +133,7 @@ in
|
|||||||
NetworkNamespacePath = "/run/netns/ovpns";
|
NetworkNamespacePath = "/run/netns/ovpns";
|
||||||
Restart = "on-failure";
|
Restart = "on-failure";
|
||||||
RestartSec = "30s";
|
RestartSec = "30s";
|
||||||
|
BindPaths = [ "/var/media" ]; #< so it can move completed torrents into the media library
|
||||||
};
|
};
|
||||||
|
|
||||||
# service to automatically backup torrents i add to transmission
|
# service to automatically backup torrents i add to transmission
|
||||||
|
@@ -43,17 +43,6 @@
|
|||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
system.activationScripts.notifyActive = {
|
|
||||||
text = ''
|
|
||||||
# send a notification to any sway users logged in, that the system has been activated/upgraded.
|
|
||||||
# this probably doesn't work if more than one sway session exists on the system.
|
|
||||||
_notifyActiveSwaySock="$(echo /run/user/*/sway-ipc*.sock)"
|
|
||||||
if [ -e "$_notifyActiveSwaySock" ]; then
|
|
||||||
SWAYSOCK="$_notifyActiveSwaySock" ${config.sane.programs.sway.packageUnwrapped}/bin/swaymsg -- exec \
|
|
||||||
"${pkgs.libnotify}/bin/notify-send 'nixos activated' 'version: $(cat $systemConfig/nixos-version)'"
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# link debug symbols into /run/current-system/sw/lib/debug
|
# link debug symbols into /run/current-system/sw/lib/debug
|
||||||
# hopefully picked up by gdb automatically?
|
# hopefully picked up by gdb automatically?
|
||||||
|
@@ -81,6 +81,7 @@ let
|
|||||||
(fromDb "feeds.simplecast.com/xKJ93w_w" // uncat) # Atlas Obscura
|
(fromDb "feeds.simplecast.com/xKJ93w_w" // uncat) # Atlas Obscura
|
||||||
(fromDb "feeds.transistor.fm/acquired" // tech)
|
(fromDb "feeds.transistor.fm/acquired" // tech)
|
||||||
(fromDb "fulltimenix.com" // tech)
|
(fromDb "fulltimenix.com" // tech)
|
||||||
|
(fromDb "hackerpublicradio.org" // tech)
|
||||||
(fromDb "lexfridman.com/podcast" // rat)
|
(fromDb "lexfridman.com/podcast" // rat)
|
||||||
(fromDb "mapspodcast.libsyn.com" // uncat) # Multidisciplinary Association for Psychedelic Studies
|
(fromDb "mapspodcast.libsyn.com" // uncat) # Multidisciplinary Association for Psychedelic Studies
|
||||||
(fromDb "omegataupodcast.net" // tech) # 3/4 German; 1/4 eps are English
|
(fromDb "omegataupodcast.net" // tech) # 3/4 German; 1/4 eps are English
|
||||||
@@ -103,6 +104,7 @@ let
|
|||||||
(fromDb "techwontsave.us" // pol) # rec by Cory Doctorow
|
(fromDb "techwontsave.us" // pol) # rec by Cory Doctorow
|
||||||
(fromDb "wakingup.libsyn.com" // pol) # Sam Harris
|
(fromDb "wakingup.libsyn.com" // pol) # Sam Harris
|
||||||
(fromDb "werenotwrong.fireside.fm" // pol)
|
(fromDb "werenotwrong.fireside.fm" // pol)
|
||||||
|
(mkPod "https://sfconservancy.org/casts/the-corresponding-source/feeds/ogg/" // tech)
|
||||||
|
|
||||||
# (fromDb "feeds.libsyn.com/421877" // rat) # Less Wrong Curated
|
# (fromDb "feeds.libsyn.com/421877" // rat) # Less Wrong Curated
|
||||||
# (fromDb "feeds.megaphone.fm/hubermanlab" // uncat) # Daniel Huberman on sleep
|
# (fromDb "feeds.megaphone.fm/hubermanlab" // uncat) # Daniel Huberman on sleep
|
||||||
@@ -121,6 +123,7 @@ let
|
|||||||
texts = [
|
texts = [
|
||||||
(fromDb "acoup.blog/feed") # history, states. author: <https://historians.social/@bretdevereaux/following>
|
(fromDb "acoup.blog/feed") # history, states. author: <https://historians.social/@bretdevereaux/following>
|
||||||
(fromDb "amosbbatto.wordpress.com" // tech)
|
(fromDb "amosbbatto.wordpress.com" // tech)
|
||||||
|
(fromDb "anish.lakhwara.com" // tech)
|
||||||
(fromDb "applieddivinitystudies.com" // rat)
|
(fromDb "applieddivinitystudies.com" // rat)
|
||||||
(fromDb "artemis.sh" // tech)
|
(fromDb "artemis.sh" // tech)
|
||||||
(fromDb "ascii.textfiles.com" // tech) # Jason Scott
|
(fromDb "ascii.textfiles.com" // tech) # Jason Scott
|
||||||
@@ -141,6 +144,7 @@ let
|
|||||||
(fromDb "edwardsnowden.substack.com" // pol // text)
|
(fromDb "edwardsnowden.substack.com" // pol // text)
|
||||||
(fromDb "fasterthanli.me" // tech)
|
(fromDb "fasterthanli.me" // tech)
|
||||||
(fromDb "gwern.net" // rat)
|
(fromDb "gwern.net" // rat)
|
||||||
|
(fromDb "hardcoresoftware.learningbyshipping.com" // tech) # Steven Sinofsky
|
||||||
(fromDb "harihareswara.net" // tech // pol) # rec by Cory Doctorow
|
(fromDb "harihareswara.net" // tech // pol) # rec by Cory Doctorow
|
||||||
(fromDb "ianthehenry.com" // tech)
|
(fromDb "ianthehenry.com" // tech)
|
||||||
(fromDb "idiomdrottning.org" // uncat)
|
(fromDb "idiomdrottning.org" // uncat)
|
||||||
@@ -157,10 +161,12 @@ let
|
|||||||
(fromDb "mg.lol" // tech)
|
(fromDb "mg.lol" // tech)
|
||||||
(fromDb "mindingourway.com" // rat)
|
(fromDb "mindingourway.com" // rat)
|
||||||
(fromDb "morningbrew.com/feed" // pol)
|
(fromDb "morningbrew.com/feed" // pol)
|
||||||
|
(fromDb "nixpkgs.news" // tech)
|
||||||
(fromDb "overcomingbias.com" // rat) # Robin Hanson
|
(fromDb "overcomingbias.com" // rat) # Robin Hanson
|
||||||
(fromDb "palladiummag.com" // uncat)
|
(fromDb "palladiummag.com" // uncat)
|
||||||
(fromDb "philosopher.coach" // rat) # Peter Saint-Andre -- side project of stpeter.im
|
(fromDb "philosopher.coach" // rat) # Peter Saint-Andre -- side project of stpeter.im
|
||||||
(fromDb "pomeroyb.com" // tech)
|
(fromDb "pomeroyb.com" // tech)
|
||||||
|
(fromDb "postmarketos.org/blog" // tech)
|
||||||
(fromDb "preposterousuniverse.com" // rat) # Sean Carroll
|
(fromDb "preposterousuniverse.com" // rat) # Sean Carroll
|
||||||
(fromDb "profectusmag.com" // uncat)
|
(fromDb "profectusmag.com" // uncat)
|
||||||
(fromDb "project-insanity.org" // tech) # shared blog by a few NixOS devs, notably onny
|
(fromDb "project-insanity.org" // tech) # shared blog by a few NixOS devs, notably onny
|
||||||
@@ -172,9 +178,11 @@ let
|
|||||||
(fromDb "sagacioussuricata.com" // tech) # ian (Sanctuary)
|
(fromDb "sagacioussuricata.com" // tech) # ian (Sanctuary)
|
||||||
(fromDb "semiaccurate.com" // tech)
|
(fromDb "semiaccurate.com" // tech)
|
||||||
(fromDb "sideways-view.com" // rat) # Paul Christiano
|
(fromDb "sideways-view.com" // rat) # Paul Christiano
|
||||||
|
(fromDb "slatecave.net" // tech)
|
||||||
(fromDb "slimemoldtimemold.com" // rat)
|
(fromDb "slimemoldtimemold.com" // rat)
|
||||||
(fromDb "spectrum.ieee.org" // tech)
|
(fromDb "spectrum.ieee.org" // tech)
|
||||||
(fromDb "stpeter.im/atom.xml" // pol)
|
(fromDb "stpeter.im/atom.xml" // pol)
|
||||||
|
(fromDb "thediff.co" // pol) # Byrne Hobart
|
||||||
# (fromDb "theregister.com" // tech)
|
# (fromDb "theregister.com" // tech)
|
||||||
(fromDb "thisweek.gnome.org" // tech)
|
(fromDb "thisweek.gnome.org" // tech)
|
||||||
(fromDb "tuxphones.com" // tech)
|
(fromDb "tuxphones.com" // tech)
|
||||||
@@ -182,23 +190,23 @@ let
|
|||||||
(fromDb "unintendedconsequenc.es" // rat)
|
(fromDb "unintendedconsequenc.es" // rat)
|
||||||
# (fromDb "vitalik.ca" // tech) # moved to vitalik.eth.limo
|
# (fromDb "vitalik.ca" // tech) # moved to vitalik.eth.limo
|
||||||
(fromDb "vitalik.eth.limo" // tech) # Vitalik Buterin
|
(fromDb "vitalik.eth.limo" // tech) # Vitalik Buterin
|
||||||
(fromDb "webcurious.co.uk" // uncat)
|
# (fromDb "webcurious.co.uk" // uncat) # link aggregator; defunct?
|
||||||
|
(fromDb "willow.phantoma.online") # wizard@xyzzy.link
|
||||||
(fromDb "xn--gckvb8fzb.com" // tech)
|
(fromDb "xn--gckvb8fzb.com" // tech)
|
||||||
(mkSubstack "astralcodexten" // rat // daily) # Scott Alexander
|
(mkSubstack "astralcodexten" // rat // daily) # Scott Alexander
|
||||||
(mkSubstack "byrnehobart" // pol // infrequent)
|
|
||||||
# (mkSubstack "doomberg" // tech // weekly) # articles are all pay-walled
|
# (mkSubstack "doomberg" // tech // weekly) # articles are all pay-walled
|
||||||
(mkSubstack "eliqian" // rat // weekly)
|
(mkSubstack "eliqian" // rat // weekly)
|
||||||
(mkSubstack "oversharing" // pol // daily)
|
(mkSubstack "oversharing" // pol // daily)
|
||||||
(mkSubstack "samkriss" // humor // infrequent)
|
(mkSubstack "samkriss" // humor // infrequent)
|
||||||
(mkText "http://benjaminrosshoffman.com/feed" // pol // weekly)
|
(mkText "http://benjaminrosshoffman.com/feed" // pol // weekly)
|
||||||
(mkText "http://boginjr.com/feed" // tech // infrequent)
|
(mkText "http://boginjr.com/feed" // tech // infrequent)
|
||||||
(mkText "https://anish.lakhwara.com/home.html" // tech // weekly)
|
|
||||||
(mkText "https://forum.merveilles.town/rss.xml" // pol // infrequent) #quality RSS list here: <https://forum.merveilles.town/thread/57/share-your-rss-feeds%21-6/>
|
(mkText "https://forum.merveilles.town/rss.xml" // pol // infrequent) #quality RSS list here: <https://forum.merveilles.town/thread/57/share-your-rss-feeds%21-6/>
|
||||||
# (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
|
# (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
|
||||||
(mkText "https://jvns.ca/atom.xml" // tech // weekly) # Julia Evans
|
(mkText "https://jvns.ca/atom.xml" // tech // weekly) # Julia Evans
|
||||||
(mkText "https://linuxphoneapps.org/blog/atom.xml" // tech // infrequent)
|
(mkText "https://linuxphoneapps.org/blog/atom.xml" // tech // infrequent)
|
||||||
(mkText "https://nixos.org/blog/announcements-rss.xml" // tech // infrequent) # 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) # more nixos stuff here, but unclear how to subscribe: <https://nixos.org/blog/categories.html>
|
||||||
(mkText "https://nixos.org/blog/stories-rss.xml" // tech // weekly)
|
(mkText "https://nixos.org/blog/stories-rss.xml" // tech // weekly)
|
||||||
|
(mkText "https://solar.lowtechmagazine.com/posts/index.xml" // tech // weekly)
|
||||||
# (mkText "https://til.simonwillison.net/tils/feed.atom" // tech // weekly)
|
# (mkText "https://til.simonwillison.net/tils/feed.atom" // tech // weekly)
|
||||||
# (mkText "https://www.bloomberg.com/opinion/authors/ARbTQlRLRjE/matthew-s-levine.rss" // pol // weekly) # Matt Levine (preview/paywalled)
|
# (mkText "https://www.bloomberg.com/opinion/authors/ARbTQlRLRjE/matthew-s-levine.rss" // pol // weekly) # Matt Levine (preview/paywalled)
|
||||||
(mkText "https://www.stratechery.com/rss" // pol // weekly) # Ben Thompson
|
(mkText "https://www.stratechery.com/rss" // pol // weekly) # Ben Thompson
|
||||||
@@ -211,6 +219,7 @@ let
|
|||||||
(fromDb "youtube.com/@Exurb1a")
|
(fromDb "youtube.com/@Exurb1a")
|
||||||
(fromDb "youtube.com/@hbomberguy")
|
(fromDb "youtube.com/@hbomberguy")
|
||||||
(fromDb "youtube.com/@JackStauber")
|
(fromDb "youtube.com/@JackStauber")
|
||||||
|
(fromDb "youtube.com/@NativLang")
|
||||||
(fromDb "youtube.com/@PolyMatter")
|
(fromDb "youtube.com/@PolyMatter")
|
||||||
# (fromDb "youtube.com/@rossmanngroup" // pol // tech) # Louis Rossmann
|
# (fromDb "youtube.com/@rossmanngroup" // pol // tech) # Louis Rossmann
|
||||||
(fromDb "youtube.com/@TechnologyConnections" // tech)
|
(fromDb "youtube.com/@TechnologyConnections" // tech)
|
||||||
@@ -223,6 +232,7 @@ let
|
|||||||
|
|
||||||
images = [
|
images = [
|
||||||
(fromDb "catandgirl.com" // img // humor)
|
(fromDb "catandgirl.com" // img // humor)
|
||||||
|
(fromDb "davidrevoy.com" // img // art)
|
||||||
(fromDb "miniature-calendar.com" // img // art // daily)
|
(fromDb "miniature-calendar.com" // img // art // daily)
|
||||||
(fromDb "pbfcomics.com" // img // humor)
|
(fromDb "pbfcomics.com" // img // humor)
|
||||||
(fromDb "poorlydrawnlines.com/feed" // img // humor)
|
(fromDb "poorlydrawnlines.com/feed" // img // humor)
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
# - x-systemd options: <https://www.freedesktop.org/software/systemd/man/systemd.mount.html>
|
# - x-systemd options: <https://www.freedesktop.org/software/systemd/man/systemd.mount.html>
|
||||||
# - fuse options: `man mount.fuse`
|
# - fuse options: `man mount.fuse`
|
||||||
|
|
||||||
{ lib, pkgs, sane-lib, ... }:
|
{ config, lib, pkgs, sane-lib, utils, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
fsOpts = rec {
|
fsOpts = rec {
|
||||||
@@ -23,15 +23,15 @@ let
|
|||||||
# N.B.: `remote-fs.target` is a dependency of multi-user.target, itself of graphical.target.
|
# N.B.: `remote-fs.target` is a dependency of multi-user.target, itself of graphical.target.
|
||||||
# hence, omitting `noauto` can slow down boots.
|
# hence, omitting `noauto` can slow down boots.
|
||||||
noauto = [ "noauto" ];
|
noauto = [ "noauto" ];
|
||||||
# lazyMount: defer mounting until first access from userspace
|
# lazyMount: defer mounting until first access from userspace.
|
||||||
|
# see: `man systemd.automount`, `man automount`, `man autofs`
|
||||||
lazyMount = noauto ++ automount;
|
lazyMount = noauto ++ automount;
|
||||||
wg = [
|
wg = [
|
||||||
"x-systemd.requires=wireguard-wg-home.service"
|
"x-systemd.requires=wireguard-wg-home.service"
|
||||||
"x-systemd.after=wireguard-wg-home.service"
|
"x-systemd.after=wireguard-wg-home.service"
|
||||||
];
|
];
|
||||||
|
|
||||||
ssh = common ++ [
|
fuse = [
|
||||||
"identityfile=/home/colin/.ssh/id_ed25519"
|
|
||||||
"allow_other" # allow users other than the one who mounts it to access it. needed, if systemd is the one mounting this fs (as root)
|
"allow_other" # allow users other than the one who mounts it to access it. needed, if systemd is the one mounting this fs (as root)
|
||||||
# allow_root: allow root to access files on this fs (if mounted by non-root, else it can always access them).
|
# allow_root: allow root to access files on this fs (if mounted by non-root, else it can always access them).
|
||||||
# N.B.: if both allow_root and allow_other are specified, then only allow_root takes effect.
|
# N.B.: if both allow_root and allow_other are specified, then only allow_root takes effect.
|
||||||
@@ -44,7 +44,18 @@ let
|
|||||||
# with default_permissions, sshfs doesn't tunnel file ops from users until checking that said user could perform said op on an equivalent local fs.
|
# with default_permissions, sshfs doesn't tunnel file ops from users until checking that said user could perform said op on an equivalent local fs.
|
||||||
"default_permissions"
|
"default_permissions"
|
||||||
];
|
];
|
||||||
sshColin = ssh ++ [
|
fuseColin = fuse ++ [
|
||||||
|
"uid=1000"
|
||||||
|
"gid=100"
|
||||||
|
];
|
||||||
|
|
||||||
|
ssh = common ++ fuse ++ [
|
||||||
|
"identityfile=/home/colin/.ssh/id_ed25519"
|
||||||
|
# i *think* idmap=user means that `colin` on `localhost` and `colin` on the remote are actually treated as the same user, even if their uid/gid differs?
|
||||||
|
# i.e., local colin's id is translated to/from remote colin's id on every operation?
|
||||||
|
"idmap=user"
|
||||||
|
];
|
||||||
|
sshColin = ssh ++ fuseColin ++ [
|
||||||
# follow_symlinks: remote files which are symlinks are presented to the local system as ordinary files (as the target of the symlink).
|
# follow_symlinks: remote files which are symlinks are presented to the local system as ordinary files (as the target of the symlink).
|
||||||
# if the symlink target does not exist, the presentation is unspecified.
|
# if the symlink target does not exist, the presentation is unspecified.
|
||||||
# symlinks which point outside the mount ARE followed. so this is more capable than `transform_symlinks`
|
# symlinks which point outside the mount ARE followed. so this is more capable than `transform_symlinks`
|
||||||
@@ -52,9 +63,6 @@ let
|
|||||||
# symlinks on the remote fs which are absolute paths are presented to the local system as relative symlinks pointing to the expected data on the remote fs.
|
# symlinks on the remote fs which are absolute paths are presented to the local system as relative symlinks pointing to the expected data on the remote fs.
|
||||||
# only symlinks which would point inside the mountpoint are translated.
|
# only symlinks which would point inside the mountpoint are translated.
|
||||||
"transform_symlinks"
|
"transform_symlinks"
|
||||||
"idmap=user"
|
|
||||||
"uid=1000"
|
|
||||||
"gid=100"
|
|
||||||
];
|
];
|
||||||
# sshRoot = ssh ++ [
|
# sshRoot = ssh ++ [
|
||||||
# # we don't transform_symlinks because that breaks the validity of remote /nix stores
|
# # we don't transform_symlinks because that breaks the validity of remote /nix stores
|
||||||
@@ -67,21 +75,43 @@ let
|
|||||||
# actimeo=n = how long (in seconds) to cache file/dir attributes (default: 3-60s)
|
# actimeo=n = how long (in seconds) to cache file/dir attributes (default: 3-60s)
|
||||||
# bg = retry failed mounts in the background
|
# bg = retry failed mounts in the background
|
||||||
# retry=n = for how many minutes `mount` will retry NFS mount operation
|
# retry=n = for how many minutes `mount` will retry NFS mount operation
|
||||||
|
# intr = allow Ctrl+C to abort I/O (it will error with `EINTR`)
|
||||||
# soft = on "major timeout", report I/O error to userspace
|
# soft = on "major timeout", report I/O error to userspace
|
||||||
|
# softreval = on "major timeout", service the request using known-stale cache results instead of erroring -- if such cache data exists
|
||||||
# retrans=n = how many times to retry a NFS request before giving userspace a "server not responding" error (default: 3)
|
# retrans=n = how many times to retry a NFS request before giving userspace a "server not responding" error (default: 3)
|
||||||
# timeo=n = number of *deciseconds* to wait for a response before retrying it (default: 600)
|
# timeo=n = number of *deciseconds* to wait for a response before retrying it (default: 600)
|
||||||
# note: client uses a linear backup, so the second request will have double this timeout, then triple, etc.
|
# note: client uses a linear backup, so the second request will have double this timeout, then triple, etc.
|
||||||
|
# proto=udp = encapsulate protocol ops inside UDP packets instead of a TCP session.
|
||||||
|
# requires `nfsvers=3` and a kernel compiled with `NFS_DISABLE_UDP_SUPPORT=n`.
|
||||||
|
# UDP might be preferable to TCP because the latter is liable to hang for ~100s (kernel TCP timeout) after a link drop.
|
||||||
|
# however, even UDP has issues with `umount` hanging.
|
||||||
|
#
|
||||||
|
# N.B.: don't change these without first testing the behavior of sandboxed apps on a flaky network.
|
||||||
nfs = common ++ [
|
nfs = common ++ [
|
||||||
# "actimeo=10"
|
# "actimeo=5"
|
||||||
"bg"
|
# "bg"
|
||||||
"retrans=4"
|
"retrans=1"
|
||||||
"retry=0"
|
"retry=0"
|
||||||
|
# "intr"
|
||||||
"soft"
|
"soft"
|
||||||
"timeo=15"
|
"softreval"
|
||||||
|
"timeo=30"
|
||||||
"nofail" # don't fail remote-fs.target when this mount fails (not an option for sshfs else would be common)
|
"nofail" # don't fail remote-fs.target when this mount fails (not an option for sshfs else would be common)
|
||||||
|
# "proto=udp" # default kernel config doesn't support NFS over UDP: <https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1964093> (see comment 11).
|
||||||
|
# "nfsvers=3" # NFSv4+ doesn't support UDP at *all*. it's ok to omit nfsvers -- server + client will negotiate v3 based on udp requirement. but omitting causes confusing mount errors when the server is *offline*, because the client defaults to v4 and thinks the udp option is a config error.
|
||||||
|
# "x-systemd.idle-timeout=10" # auto-unmount after this much inactivity
|
||||||
|
];
|
||||||
|
|
||||||
|
# manually perform a ftp mount via e.g.
|
||||||
|
# curlftpfs -o ftpfs_debug=2,user=anonymous:anonymous,connect_timeout=10 -f -s ftp://servo-hn /mnt/my-ftp
|
||||||
|
ftp = common ++ fuseColin ++ [
|
||||||
|
# "ftpfs_debug=2"
|
||||||
|
"user=colin:ipauth"
|
||||||
|
"connect_timeout=10"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
remoteHome = host: {
|
remoteHome = host: {
|
||||||
|
sane.programs.sshfs-fuse.enableFor.system = true;
|
||||||
fileSystems."/mnt/${host}/home" = {
|
fileSystems."/mnt/${host}/home" = {
|
||||||
device = "colin@${host}:/home/colin";
|
device = "colin@${host}:/home/colin";
|
||||||
fsType = "fuse.sshfs";
|
fsType = "fuse.sshfs";
|
||||||
@@ -94,6 +124,54 @@ let
|
|||||||
dir.acl.mode = "0700";
|
dir.acl.mode = "0700";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
remoteServo = subdir: {
|
||||||
|
sane.programs.curlftpfs.enableFor.system = true;
|
||||||
|
sane.fs."/mnt/servo/${subdir}" = sane-lib.fs.wanted {
|
||||||
|
dir.acl.user = "colin";
|
||||||
|
dir.acl.group = "users";
|
||||||
|
dir.acl.mode = "0750";
|
||||||
|
};
|
||||||
|
fileSystems."/mnt/servo/${subdir}" = {
|
||||||
|
device = "ftp://servo-hn:/${subdir}";
|
||||||
|
noCheck = true;
|
||||||
|
fsType = "fuse.curlftpfs";
|
||||||
|
options = fsOpts.ftp ++ fsOpts.noauto ++ fsOpts.wg;
|
||||||
|
# fsType = "nfs";
|
||||||
|
# options = fsOpts.nfs ++ fsOpts.lazyMount ++ fsOpts.wg;
|
||||||
|
};
|
||||||
|
systemd.services."automount-servo-${utils.escapeSystemdPath subdir}" = let
|
||||||
|
fs = config.fileSystems."/mnt/servo/${subdir}";
|
||||||
|
in {
|
||||||
|
# this is a *flaky* network mount, especially on moby.
|
||||||
|
# if done as a normal autofs mount, access will eternally block when network is dropped.
|
||||||
|
# notably, this would block *any* sandboxed app which allows media access, whether they actually try to use that media or not.
|
||||||
|
# a practical solution is this: mount as a service -- instead of autofs -- and unmount on timeout error, in a restart loop.
|
||||||
|
# until the ftp handshake succeeds, nothing is actually mounted to the vfs, so this doesn't slow down any I/O when network is down.
|
||||||
|
description = "automount /mnt/servo/${subdir} in a fault-tolerant and non-blocking manner";
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
requires = [ "network-online.target" ];
|
||||||
|
wantedBy = [ "default.target" ];
|
||||||
|
|
||||||
|
serviceConfig.Type = "simple";
|
||||||
|
serviceConfig.ExecStart = lib.escapeShellArgs [
|
||||||
|
"/usr/bin/env"
|
||||||
|
"PATH=/run/current-system/sw/bin"
|
||||||
|
"mount.${fs.fsType}"
|
||||||
|
"-f" # foreground (i.e. don't daemonize)
|
||||||
|
"-s" # single-threaded (TODO: it's probably ok to disable this?)
|
||||||
|
"-o"
|
||||||
|
(lib.concatStringsSep "," (lib.filter (o: !lib.hasPrefix "x-systemd." o) fs.options))
|
||||||
|
fs.device
|
||||||
|
"/mnt/servo/${subdir}"
|
||||||
|
];
|
||||||
|
# not sure if this configures a linear, or exponential backoff.
|
||||||
|
# but the first restart will be after `RestartSec`, and the n'th restart (n = RestartSteps) will be RestartMaxDelaySec after the n-1'th exit.
|
||||||
|
serviceConfig.Restart = "always";
|
||||||
|
serviceConfig.RestartSec = "10s";
|
||||||
|
serviceConfig.RestartMaxDelaySec = "120s";
|
||||||
|
serviceConfig.RestartSteps = "5";
|
||||||
|
};
|
||||||
|
};
|
||||||
in
|
in
|
||||||
lib.mkMerge [
|
lib.mkMerge [
|
||||||
{
|
{
|
||||||
@@ -128,35 +206,6 @@ lib.mkMerge [
|
|||||||
# but it decreases working memory under the heaviest of loads by however much space the compressed memory occupies (e.g. 50% if 2:1; 25% if 4:1)
|
# but it decreases working memory under the heaviest of loads by however much space the compressed memory occupies (e.g. 50% if 2:1; 25% if 4:1)
|
||||||
zramSwap.memoryPercent = 100;
|
zramSwap.memoryPercent = 100;
|
||||||
|
|
||||||
# fileSystems."/mnt/servo-nfs" = {
|
|
||||||
# device = "servo-hn:/";
|
|
||||||
# noCheck = true;
|
|
||||||
# fsType = "nfs";
|
|
||||||
# options = fsOpts.nfs ++ fsOpts.automount ++ fsOpts.wg;
|
|
||||||
# };
|
|
||||||
fileSystems."/mnt/servo/media" = {
|
|
||||||
device = "servo-hn:/media";
|
|
||||||
noCheck = true;
|
|
||||||
fsType = "nfs";
|
|
||||||
options = fsOpts.nfs ++ fsOpts.lazyMount ++ fsOpts.wg;
|
|
||||||
};
|
|
||||||
sane.fs."/mnt/servo/media" = sane-lib.fs.wanted {
|
|
||||||
dir.acl.user = "colin";
|
|
||||||
dir.acl.group = "users";
|
|
||||||
dir.acl.mode = "0750";
|
|
||||||
};
|
|
||||||
fileSystems."/mnt/servo/playground" = {
|
|
||||||
device = "servo-hn:/playground";
|
|
||||||
noCheck = true;
|
|
||||||
fsType = "nfs";
|
|
||||||
options = fsOpts.nfs ++ fsOpts.lazyMount ++ fsOpts.wg;
|
|
||||||
};
|
|
||||||
sane.fs."/mnt/servo/playground" = sane-lib.fs.wanted {
|
|
||||||
dir.acl.user = "colin";
|
|
||||||
dir.acl.group = "users";
|
|
||||||
dir.acl.mode = "0750";
|
|
||||||
};
|
|
||||||
|
|
||||||
# environment.pathsToLink = [
|
# environment.pathsToLink = [
|
||||||
# # needed to achieve superuser access for user-mounted filesystems (see sshRoot above)
|
# # needed to achieve superuser access for user-mounted filesystems (see sshRoot above)
|
||||||
# # we can only link whole directories here, even though we're only interested in pkgs.openssh
|
# # we can only link whole directories here, even though we're only interested in pkgs.openssh
|
||||||
@@ -164,13 +213,23 @@ lib.mkMerge [
|
|||||||
# ];
|
# ];
|
||||||
|
|
||||||
programs.fuse.userAllowOther = true; #< necessary for `allow_other` or `allow_root` options.
|
programs.fuse.userAllowOther = true; #< necessary for `allow_other` or `allow_root` options.
|
||||||
environment.systemPackages = [
|
|
||||||
pkgs.sshfs-fuse
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(remoteHome "desko")
|
(remoteHome "desko")
|
||||||
(remoteHome "lappy")
|
(remoteHome "lappy")
|
||||||
(remoteHome "moby")
|
(remoteHome "moby")
|
||||||
|
# this granularity of servo media mounts is necessary to support sandboxing:
|
||||||
|
# for flaky mounts, we can only bind the mountpoint itself into the sandbox,
|
||||||
|
# so it's either this or unconditionally bind all of media/.
|
||||||
|
(remoteServo "media/archive")
|
||||||
|
(remoteServo "media/Books")
|
||||||
|
(remoteServo "media/collections")
|
||||||
|
# (remoteServo "media/datasets")
|
||||||
|
(remoteServo "media/freeleech")
|
||||||
|
(remoteServo "media/games")
|
||||||
|
(remoteServo "media/Music")
|
||||||
|
(remoteServo "media/Pictures/macros")
|
||||||
|
(remoteServo "media/Videos")
|
||||||
|
(remoteServo "playground")
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@@ -35,6 +35,16 @@
|
|||||||
# servo needs zfs though, which doesn't support every kernel.
|
# servo needs zfs though, which doesn't support every kernel.
|
||||||
boot.kernelPackages = lib.mkDefault pkgs.zfs.latestCompatibleLinuxPackages;
|
boot.kernelPackages = lib.mkDefault pkgs.zfs.latestCompatibleLinuxPackages;
|
||||||
|
|
||||||
|
# TODO: remove after linux 6.9. see: <https://github.com/axboe/liburing/issues/1113>
|
||||||
|
# - <https://github.com/neovim/neovim/issues/28149>
|
||||||
|
# - <https://git.kernel.dk/cgit/linux/commit/?h=io_uring-6.9&id=e5444baa42e545bb929ba56c497e7f3c73634099>
|
||||||
|
# when removing, try starting and suspending (ctrl+z) two instances of neovim simultaneously.
|
||||||
|
# if the system doesn't freeze, then this is safe to remove.
|
||||||
|
# added 2024-04-04
|
||||||
|
sane.user.fs.".profile".symlink.text = lib.mkBefore ''
|
||||||
|
export UV_USE_IO_URING=0
|
||||||
|
'';
|
||||||
|
|
||||||
# hack in the `boot.shell_on_fail` arg since that doesn't always seem to work.
|
# hack in the `boot.shell_on_fail` arg since that doesn't always seem to work.
|
||||||
boot.initrd.preFailCommands = "allowShell=1";
|
boot.initrd.preFailCommands = "allowShell=1";
|
||||||
|
|
||||||
@@ -79,11 +89,6 @@
|
|||||||
HandleLidSwitch=lock
|
HandleLidSwitch=lock
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# some packages build only if binfmt *isn't* present
|
|
||||||
nix.settings.system-features = lib.mkIf (config.boot.binfmt.emulatedSystems == []) [
|
|
||||||
"no-binfmt"
|
|
||||||
];
|
|
||||||
|
|
||||||
# services.snapper.configs = {
|
# services.snapper.configs = {
|
||||||
# root = {
|
# root = {
|
||||||
# subvolume = "/";
|
# subvolume = "/";
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
{ config, ... }:
|
{ config, lib, ... }:
|
||||||
{
|
{
|
||||||
sane.user.persist.byStore.plaintext = [
|
sane.user.persist.byStore.plaintext = [
|
||||||
"archive"
|
"archive"
|
||||||
@@ -29,14 +29,17 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
# convenience
|
# convenience
|
||||||
sane.user.fs.".persist/private".symlink.target = config.sane.persist.stores.private.origin;
|
sane.user.fs = let
|
||||||
sane.user.fs.".persist/plaintext".symlink.target = config.sane.persist.stores.plaintext.origin;
|
persistEnabled = config.sane.persist.enable;
|
||||||
sane.user.fs.".persist/ephemeral".symlink.target = config.sane.persist.stores.cryptClearOnBoot.origin;
|
in {
|
||||||
|
".persist/private" = lib.mkIf persistEnabled { symlink.target = config.sane.persist.stores.private.origin; };
|
||||||
|
".persist/plaintext" = lib.mkIf persistEnabled { symlink.target = config.sane.persist.stores.plaintext.origin; };
|
||||||
|
".persist/ephemeral" = lib.mkIf persistEnabled { symlink.target = config.sane.persist.stores.cryptClearOnBoot.origin; };
|
||||||
|
|
||||||
sane.user.fs."nixos".symlink.target = "dev/nixos";
|
"nixos".symlink.target = "dev/nixos";
|
||||||
|
|
||||||
sane.user.fs."Books/servo".symlink.target = "/mnt/servo/media/Books";
|
"Books/servo".symlink.target = "/mnt/servo/media/Books";
|
||||||
sane.user.fs."Videos/servo".symlink.target = "/mnt/servo/media/Videos";
|
"Videos/servo".symlink.target = "/mnt/servo/media/Videos";
|
||||||
# sane.user.fs."Music/servo".symlink.target = "/mnt/servo/media/Music";
|
"Pictures/servo-macros".symlink.target = "/mnt/servo/media/Pictures/macros";
|
||||||
sane.user.fs."Pictures/servo-macros".symlink.target = "/mnt/servo/media/Pictures/macros";
|
};
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
XDG_MUSIC_DIR="$HOME/Music"
|
XDG_MUSIC_DIR="$HOME/Music"
|
||||||
XDG_PICTURES_DIR="$HOME/Pictures"
|
XDG_PICTURES_DIR="$HOME/Pictures"
|
||||||
XDG_PUBLICSHARE_DIR="$HOME/.xdg/Public"
|
XDG_PUBLICSHARE_DIR="$HOME/.xdg/Public"
|
||||||
|
XDG_SCREENSHOTS_DIR="$HOME/Pictures/Screenshots"
|
||||||
XDG_TEMPLATES_DIR="$HOME/.xdg/Templates"
|
XDG_TEMPLATES_DIR="$HOME/.xdg/Templates"
|
||||||
XDG_VIDEOS_DIR="$HOME/Videos"
|
XDG_VIDEOS_DIR="$HOME/Videos"
|
||||||
'';
|
'';
|
||||||
@@ -17,4 +18,12 @@
|
|||||||
# prevent `xdg-user-dirs-update` from overriding/updating our config
|
# prevent `xdg-user-dirs-update` from overriding/updating our config
|
||||||
# see <https://manpages.ubuntu.com/manpages/bionic/man5/user-dirs.conf.5.html>
|
# see <https://manpages.ubuntu.com/manpages/bionic/man5/user-dirs.conf.5.html>
|
||||||
sane.user.fs.".config/user-dirs.conf".symlink.text = "enabled=False";
|
sane.user.fs.".config/user-dirs.conf".symlink.text = "enabled=False";
|
||||||
|
|
||||||
|
sane.user.fs.".profile".symlink.text = ''
|
||||||
|
# configure XDG_<type>_DIR preferences (e.g. for downloads, screenshots, etc)
|
||||||
|
# surround with `set -o allexport` since user-dirs.dirs doesn't `export` its vars
|
||||||
|
set -a
|
||||||
|
source $HOME/.config/user-dirs.dirs
|
||||||
|
set +a
|
||||||
|
'';
|
||||||
}
|
}
|
||||||
|
@@ -53,8 +53,9 @@
|
|||||||
|
|
||||||
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages.
|
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages.
|
||||||
# this is actually a no-op, and the real action happens in assigning `nix.settings.nix-path`.
|
# this is actually a no-op, and the real action happens in assigning `nix.settings.nix-path`.
|
||||||
nix.nixPath = [
|
nix.nixPath = (lib.optionals config.sane.enableSlowPrograms [
|
||||||
"nixpkgs=${pkgs.path}"
|
"nixpkgs=${pkgs.path}"
|
||||||
|
]) ++ [
|
||||||
# note the import starts at repo root: this allows `./overlay/default.nix` to access the stuff at the root
|
# 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"
|
# "nixpkgs-overlays=${../../..}/hosts/common/nix-path/overlay"
|
||||||
# as long as my system itself doesn't rely on NIXPKGS at runtime, we can point the overlays to git
|
# as long as my system itself doesn't rely on NIXPKGS at runtime, we can point the overlays to git
|
||||||
@@ -63,7 +64,13 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
# ensure new deployments have a source of this repo with which they can bootstrap.
|
# ensure new deployments have a source of this repo with which they can bootstrap.
|
||||||
environment.etc."nixos".source = ../../..;
|
# this however changes on every commit and can be slow to copy for e.g. `moby`.
|
||||||
|
environment.etc."nixos" = lib.mkIf config.sane.enableSlowPrograms {
|
||||||
|
source = ../../..;
|
||||||
|
};
|
||||||
|
environment.etc."nix/registry.json" = lib.mkIf (!config.sane.enableSlowPrograms) {
|
||||||
|
enable = false;
|
||||||
|
};
|
||||||
|
|
||||||
systemd.services.nix-daemon.serviceConfig = {
|
systemd.services.nix-daemon.serviceConfig = {
|
||||||
# the nix-daemon manages nix builders
|
# the nix-daemon manages nix builders
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
"/run/current-system/sw"
|
"/run/current-system/sw"
|
||||||
];
|
];
|
||||||
|
|
||||||
# NIXPKGS_CONFIG defaults to "/etc/nix/nixpkgs-config.nix", for idfk why.
|
# NIXPKGS_CONFIG defaults to "/etc/nix/nixpkgs-config.nix" in <nixos/modules/programs/environment.nix>.
|
||||||
# that's never existed on my system and everything does fine without it set empty (no nixpkgs API to forcibly *unset* it).
|
# that's never existed on my system and everything does fine without it set empty (no nixpkgs API to forcibly *unset* it).
|
||||||
environment.variables.NIXPKGS_CONFIG = lib.mkForce "";
|
environment.variables.NIXPKGS_CONFIG = lib.mkForce "";
|
||||||
# XDG_CONFIG_DIRS defaults to "/etc/xdg", which doesn't exist.
|
# XDG_CONFIG_DIRS defaults to "/etc/xdg", which doesn't exist.
|
||||||
@@ -42,4 +42,31 @@
|
|||||||
# so as to inform when trying to run a non-nixos binary?
|
# so as to inform when trying to run a non-nixos binary?
|
||||||
# IMO that's confusing: i thought /lib/ld-linux.so was some file actually required by nix.
|
# IMO that's confusing: i thought /lib/ld-linux.so was some file actually required by nix.
|
||||||
environment.stub-ld.enable = false;
|
environment.stub-ld.enable = false;
|
||||||
|
|
||||||
|
# `less.enable` sets LESSKEYIN_SYSTEM, LESSOPEN, LESSCLOSE env vars, which does confusing "lesspipe" things, so disable that.
|
||||||
|
# it's enabled by default from `<nixos/modules/programs/environment.nix>`, who also sets `PAGER="less"` and `EDITOR="nano"` (keep).
|
||||||
|
programs.less.enable = lib.mkForce false;
|
||||||
|
environment.variables.PAGER = lib.mkOverride 900 ""; # mkDefault sets 1000. non-override is 100. 900 will beat the nixpkgs `mkDefault` but not anyone else.
|
||||||
|
environment.variables.EDITOR = lib.mkOverride 900 "";
|
||||||
|
|
||||||
|
# several packages (dconf, modemmanager, networkmanager, gvfs, polkit, udisks, bluez/blueman, feedbackd, etc)
|
||||||
|
# will add themselves to the dbus search path.
|
||||||
|
# i prefer dbus to only search XDG paths (/share/dbus-1) for service files, as that's more introspectable.
|
||||||
|
# see: <repo:nixos/nixpkgs:nixos/modules/services/system/dbus.nix>
|
||||||
|
# TODO: sandbox dbus? i pretty explicitly don't want to use it as a launcher.
|
||||||
|
services.dbus.packages = lib.mkForce [
|
||||||
|
"/run/current-system/sw"
|
||||||
|
# config.system.path
|
||||||
|
# pkgs.dbus
|
||||||
|
# pkgs.polkit.out
|
||||||
|
# pkgs.modemmanager
|
||||||
|
# pkgs.networkmanager
|
||||||
|
# pkgs.udisks
|
||||||
|
# pkgs.wpa_supplicant
|
||||||
|
];
|
||||||
|
|
||||||
|
# systemd by default forces shitty defaults for e.g. /tmp/.X11-unix.
|
||||||
|
# nixos propagates those in: <nixos/modules/system/boot/systemd/tmpfiles.nix>
|
||||||
|
# by overwriting this with an empty file, we can effectively remove it.
|
||||||
|
environment.etc."tmpfiles.d/x11.conf".text = "# (removed by Colin)";
|
||||||
}
|
}
|
||||||
|
@@ -87,13 +87,8 @@ in
|
|||||||
|
|
||||||
services.abaddon = {
|
services.abaddon = {
|
||||||
description = "unofficial Discord chat client";
|
description = "unofficial Discord chat client";
|
||||||
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
|
partOf = lib.mkIf cfg.config.autostart [ "graphical-session" ];
|
||||||
serviceConfig = {
|
command = "abaddon";
|
||||||
ExecStart = "${cfg.package}/bin/abaddon";
|
|
||||||
Type = "simple";
|
|
||||||
Restart = "always";
|
|
||||||
RestartSec = "20s";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
{
|
{
|
||||||
sane.programs.aerc = {
|
sane.programs.aerc = {
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "inplace";
|
sandbox.wrapperType = "inplace"; #< /share/aerc/aerc.conf refers to other /share files by absolute path
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
secrets.".config/aerc/accounts.conf" = ../../../secrets/common/aerc_accounts.conf.bin;
|
secrets.".config/aerc/accounts.conf" = ../../../secrets/common/aerc_accounts.conf.bin;
|
||||||
mime.associations."x-scheme-handler/mailto" = "aerc.desktop";
|
mime.associations."x-scheme-handler/mailto" = "aerc.desktop";
|
||||||
|
@@ -3,14 +3,28 @@
|
|||||||
# - `man 5 alacritty`
|
# - `man 5 alacritty`
|
||||||
# - defaults: <https://github.com/alacritty/alacritty/releases> -> alacritty.yml
|
# - defaults: <https://github.com/alacritty/alacritty/releases> -> alacritty.yml
|
||||||
# - irc: #alacritty on libera.chat
|
# - irc: #alacritty on libera.chat
|
||||||
{ lib, ... }:
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.sane.programs.alacritty;
|
||||||
|
in
|
||||||
{
|
{
|
||||||
sane.programs.alacritty = {
|
sane.programs.alacritty = {
|
||||||
|
configOption = with lib; mkOption {
|
||||||
|
default = {};
|
||||||
|
type = types.submodule {
|
||||||
|
options.fontSize = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
default = 14;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
sandbox.enable = false;
|
sandbox.enable = false;
|
||||||
env.TERMINAL = lib.mkDefault "alacritty";
|
env.TERMINAL = lib.mkDefault "alacritty";
|
||||||
|
|
||||||
fs.".config/alacritty/alacritty.toml".symlink.text = ''
|
fs.".config/alacritty/alacritty.toml".symlink.text = ''
|
||||||
[font]
|
[font]
|
||||||
size = 14
|
size = ${builtins.toString cfg.config.fontSize}
|
||||||
|
|
||||||
[[keyboard.bindings]]
|
[[keyboard.bindings]]
|
||||||
mods = "Control"
|
mods = "Control"
|
||||||
@@ -36,6 +50,21 @@
|
|||||||
mods = "Control|Shift"
|
mods = "Control|Shift"
|
||||||
key = "PageDown"
|
key = "PageDown"
|
||||||
action = "ScrollPageDown"
|
action = "ScrollPageDown"
|
||||||
|
|
||||||
|
# disable OS shortcuts which leak through...
|
||||||
|
# see sway config or sane-input-handler for more info on why these leak through
|
||||||
|
[[keyboard.bindings]]
|
||||||
|
key = "AudioVolumeUp"
|
||||||
|
action = "None"
|
||||||
|
[[keyboard.bindings]]
|
||||||
|
key = "AudioVolumeDown"
|
||||||
|
action = "None"
|
||||||
|
[[keyboard.bindings]]
|
||||||
|
key = "Power"
|
||||||
|
action = "None"
|
||||||
|
[[keyboard.bindings]]
|
||||||
|
key = "PowerOff"
|
||||||
|
action = "None"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
65
hosts/common/programs/alsa-ucm-conf/default.nix
Normal file
65
hosts/common/programs/alsa-ucm-conf/default.nix
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.sane.programs.alsa-ucm-conf;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sane.programs.alsa-ucm-conf = {
|
||||||
|
configOption = with lib; mkOption {
|
||||||
|
default = {};
|
||||||
|
type = types.submodule {
|
||||||
|
options.preferEarpiece = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# upstream alsa ships with PinePhone audio configs, but they don't actually produce sound.
|
||||||
|
# see: <https://github.com/alsa-project/alsa-ucm-conf/pull/134>
|
||||||
|
# these audio files come from some revision of:
|
||||||
|
# - <https://gitlab.manjaro.org/manjaro-arm/packages/community/phosh/alsa-ucm-pinephone>
|
||||||
|
#
|
||||||
|
# alternative to patching is to plumb `ALSA_CONFIG_UCM2 = "${./ucm2}"` environment variable into the relevant places
|
||||||
|
# e.g. `systemd.services.pulseaudio.environment`.
|
||||||
|
# that leaves more opportunity for gaps (i.e. missing a service),
|
||||||
|
# on the other hand this method causes about 500 packages to be rebuilt (including qt5 and webkitgtk).
|
||||||
|
#
|
||||||
|
# note that with these files, the following audio device support:
|
||||||
|
# - headphones work.
|
||||||
|
# - "internal earpiece" works.
|
||||||
|
# - "internal speaker" doesn't work (but that's probably because i broke the ribbon cable)
|
||||||
|
# - "analog output" doesn't work.
|
||||||
|
packageUnwrapped = pkgs.alsa-ucm-conf.overrideAttrs (upstream: {
|
||||||
|
postPatch = (upstream.postPatch or "") + ''
|
||||||
|
cp ${./ucm2/PinePhone}/* ucm2/Allwinner/A64/PinePhone/
|
||||||
|
|
||||||
|
# fix the self-contained ucm files i source from to have correct path within the alsa-ucm-conf source tree
|
||||||
|
substituteInPlace ucm2/Allwinner/A64/PinePhone/PinePhone.conf \
|
||||||
|
--replace-fail 'HiFi.conf' '/Allwinner/A64/PinePhone/HiFi.conf'
|
||||||
|
substituteInPlace ucm2/Allwinner/A64/PinePhone/PinePhone.conf \
|
||||||
|
--replace-fail 'VoiceCall.conf' '/Allwinner/A64/PinePhone/VoiceCall.conf'
|
||||||
|
'' + lib.optionalString cfg.config.preferEarpiece ''
|
||||||
|
# decrease the priority of the internal speaker so that sounds are routed
|
||||||
|
# to the earpiece by default.
|
||||||
|
# this is just personal preference.
|
||||||
|
substituteInPlace ucm2/Allwinner/A64/PinePhone/{HiFi.conf,VoiceCall.conf} \
|
||||||
|
--replace-fail 'PlaybackPriority 300' 'PlaybackPriority 100'
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
|
||||||
|
sandbox.enable = false; #< only provides #out/share/alsa
|
||||||
|
|
||||||
|
# alsa-lib package only looks in its $out/share/alsa to find runtime config data, by default.
|
||||||
|
# but ALSA_CONFIG_UCM2 is an env var that can override that.
|
||||||
|
# this is particularly needed by wireplumber;
|
||||||
|
# also *maybe* pipewire and pipewire-pulse.
|
||||||
|
# taken from <repo:nixos/mobile-nixos:modules/quirks/audio.nix>
|
||||||
|
env.ALSA_CONFIG_UCM2 = "/run/current-system/sw/share/alsa/ucm2";
|
||||||
|
|
||||||
|
enableFor.system = lib.mkIf (builtins.any (en: en) (builtins.attrValues cfg.enableFor.user)) true;
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.pathsToLink = lib.mkIf cfg.enabled [
|
||||||
|
"/share/alsa/ucm2"
|
||||||
|
];
|
||||||
|
}
|
@@ -31,7 +31,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
persist.byStore.plaintext = [
|
persist.byStore.plaintext = [
|
||||||
|
@@ -62,6 +62,7 @@ in
|
|||||||
# "iw"
|
# "iw"
|
||||||
"jq"
|
"jq"
|
||||||
"killall"
|
"killall"
|
||||||
|
"less"
|
||||||
# "libcap_ng" # for `netcap`
|
# "libcap_ng" # for `netcap`
|
||||||
"lsof"
|
"lsof"
|
||||||
# "miniupnpc"
|
# "miniupnpc"
|
||||||
@@ -78,6 +79,7 @@ in
|
|||||||
"powertop"
|
"powertop"
|
||||||
"pstree"
|
"pstree"
|
||||||
"ripgrep"
|
"ripgrep"
|
||||||
|
"s6-rc" # service manager
|
||||||
"screen"
|
"screen"
|
||||||
"smartmontools" # smartctl
|
"smartmontools" # smartctl
|
||||||
# "socat"
|
# "socat"
|
||||||
@@ -121,6 +123,7 @@ in
|
|||||||
# "gopass"
|
# "gopass"
|
||||||
# "gopass-jsonapi"
|
# "gopass-jsonapi"
|
||||||
# "helix" # text editor
|
# "helix" # text editor
|
||||||
|
"htop" # needed as a user package, for ~/.config/htop
|
||||||
# "libsecret" # for managing user keyrings (secret-tool)
|
# "libsecret" # for managing user keyrings (secret-tool)
|
||||||
# "lm_sensors" # for sensors-detect
|
# "lm_sensors" # for sensors-detect
|
||||||
# "lshw"
|
# "lshw"
|
||||||
@@ -165,6 +168,7 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
consoleMediaUtils = declPackageSet [
|
consoleMediaUtils = declPackageSet [
|
||||||
|
"blast-ugjka" # cast audio to UPNP/DLNA devices (via pulseaudio sink)
|
||||||
# "catt" # cast videos to chromecast
|
# "catt" # cast videos to chromecast
|
||||||
"ffmpeg"
|
"ffmpeg"
|
||||||
"go2tv" # cast videos to UPNP/DLNA device (i.e. tv).
|
"go2tv" # cast videos to UPNP/DLNA device (i.e. tv).
|
||||||
@@ -203,17 +207,17 @@ in
|
|||||||
# INDIVIDUAL PACKAGE DEFINITIONS
|
# INDIVIDUAL PACKAGE DEFINITIONS
|
||||||
|
|
||||||
alsaUtils.sandbox.method = "landlock";
|
alsaUtils.sandbox.method = "landlock";
|
||||||
alsaUtils.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
alsaUtils.sandbox.whitelistAudio = true; #< not strictly necessary?
|
alsaUtils.sandbox.whitelistAudio = true; #< not strictly necessary?
|
||||||
|
|
||||||
|
backblaze-b2 = {};
|
||||||
|
|
||||||
blanket.sandbox.method = "bwrap";
|
blanket.sandbox.method = "bwrap";
|
||||||
blanket.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
blanket.sandbox.whitelistAudio = true;
|
blanket.sandbox.whitelistAudio = true;
|
||||||
# blanket.sandbox.whitelistDbus = [ "user" ]; # TODO: untested
|
# blanket.sandbox.whitelistDbus = [ "user" ]; # TODO: untested
|
||||||
blanket.sandbox.whitelistWayland = true;
|
blanket.sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
blueberry.sandbox.method = "bwrap";
|
blueberry.sandbox.method = "bwrap";
|
||||||
blueberry.sandbox.wrapperType = "inplace"; # /etc/xdg/autostart hardcodes paths
|
blueberry.sandbox.wrapperType = "inplace"; #< various /lib scripts refer to the bins by full path
|
||||||
blueberry.sandbox.whitelistWayland = true;
|
blueberry.sandbox.whitelistWayland = true;
|
||||||
blueberry.sandbox.extraPaths = [
|
blueberry.sandbox.extraPaths = [
|
||||||
"/dev/rfkill"
|
"/dev/rfkill"
|
||||||
@@ -223,11 +227,9 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
bridge-utils.sandbox.method = "bwrap"; #< bwrap, landlock: both work
|
bridge-utils.sandbox.method = "bwrap"; #< bwrap, landlock: both work
|
||||||
bridge-utils.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
bridge-utils.sandbox.net = "all";
|
bridge-utils.sandbox.net = "all";
|
||||||
|
|
||||||
brightnessctl.sandbox.method = "landlock"; # also bwrap, but landlock is more responsive
|
brightnessctl.sandbox.method = "landlock"; # also bwrap, but landlock is more responsive
|
||||||
brightnessctl.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
brightnessctl.sandbox.extraPaths = [
|
brightnessctl.sandbox.extraPaths = [
|
||||||
"/sys/class/backlight"
|
"/sys/class/backlight"
|
||||||
"/sys/class/leds"
|
"/sys/class/leds"
|
||||||
@@ -236,16 +238,16 @@ in
|
|||||||
brightnessctl.sandbox.whitelistDbus = [ "system" ];
|
brightnessctl.sandbox.whitelistDbus = [ "system" ];
|
||||||
|
|
||||||
btrfs-progs.sandbox.method = "bwrap"; #< bwrap, landlock: both work
|
btrfs-progs.sandbox.method = "bwrap"; #< bwrap, landlock: both work
|
||||||
btrfs-progs.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
btrfs-progs.sandbox.autodetectCliPaths = "existing"; # e.g. `btrfs filesystem df /my/fs`
|
btrfs-progs.sandbox.autodetectCliPaths = "existing"; # e.g. `btrfs filesystem df /my/fs`
|
||||||
|
|
||||||
"cacert.unbundled".sandbox.enable = false;
|
"cacert.unbundled".sandbox.enable = false;
|
||||||
|
|
||||||
cargo.persist.byStore.plaintext = [ ".cargo" ];
|
cargo.persist.byStore.plaintext = [ ".cargo" ];
|
||||||
|
|
||||||
|
clang = {};
|
||||||
|
|
||||||
# cryptsetup: typical use is `cryptsetup open /dev/loopxyz mappedName`, and creates `/dev/mapper/mappedName`
|
# cryptsetup: typical use is `cryptsetup open /dev/loopxyz mappedName`, and creates `/dev/mapper/mappedName`
|
||||||
cryptsetup.sandbox.method = "landlock";
|
cryptsetup.sandbox.method = "landlock";
|
||||||
cryptsetup.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
cryptsetup.sandbox.extraPaths = [
|
cryptsetup.sandbox.extraPaths = [
|
||||||
"/dev/mapper"
|
"/dev/mapper"
|
||||||
"/dev/random"
|
"/dev/random"
|
||||||
@@ -259,12 +261,10 @@ in
|
|||||||
cryptsetup.sandbox.autodetectCliPaths = "existing";
|
cryptsetup.sandbox.autodetectCliPaths = "existing";
|
||||||
|
|
||||||
ddrescue.sandbox.method = "landlock"; # TODO:sandbox: untested
|
ddrescue.sandbox.method = "landlock"; # TODO:sandbox: untested
|
||||||
ddrescue.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
ddrescue.sandbox.autodetectCliPaths = "existingOrParent";
|
ddrescue.sandbox.autodetectCliPaths = "existingOrParent";
|
||||||
|
|
||||||
# auth token, preferences
|
# auth token, preferences
|
||||||
delfin.sandbox.method = "bwrap";
|
delfin.sandbox.method = "bwrap";
|
||||||
delfin.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
delfin.sandbox.whitelistAudio = true;
|
delfin.sandbox.whitelistAudio = true;
|
||||||
delfin.sandbox.whitelistDbus = [ "user" ]; # else `mpris` plugin crashes the player
|
delfin.sandbox.whitelistDbus = [ "user" ]; # else `mpris` plugin crashes the player
|
||||||
delfin.sandbox.whitelistDri = true;
|
delfin.sandbox.whitelistDri = true;
|
||||||
@@ -273,45 +273,54 @@ in
|
|||||||
delfin.persist.byStore.private = [ ".config/delfin" ];
|
delfin.persist.byStore.private = [ ".config/delfin" ];
|
||||||
|
|
||||||
dig.sandbox.method = "bwrap";
|
dig.sandbox.method = "bwrap";
|
||||||
dig.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
dig.sandbox.net = "all";
|
dig.sandbox.net = "all";
|
||||||
|
|
||||||
# creds, but also 200 MB of node modules, etc
|
# creds, but also 200 MB of node modules, etc
|
||||||
|
discord.persist.byStore.private = [ ".config/discord" ];
|
||||||
|
discord.suggestedPrograms = [ "xwayland" ];
|
||||||
discord.sandbox.method = "bwrap";
|
discord.sandbox.method = "bwrap";
|
||||||
discord.sandbox.wrapperType = "inplace"; #< /opt-style packaging
|
discord.sandbox.wrapperType = "inplace"; #< /opt-style packaging
|
||||||
discord.sandbox.whitelistAudio = true;
|
discord.sandbox.whitelistAudio = true;
|
||||||
discord.sandbox.whitelistDbus = [ "user" ]; # needed for xdg-open
|
discord.sandbox.whitelistDbus = [ "user" ]; # needed for xdg-open
|
||||||
discord.sandbox.whitelistWayland = true;
|
discord.sandbox.whitelistWayland = true;
|
||||||
|
discord.sandbox.whitelistX = true;
|
||||||
discord.sandbox.net = "clearnet";
|
discord.sandbox.net = "clearnet";
|
||||||
discord.persist.byStore.private = [ ".config/discord" ];
|
discord.sandbox.extraHomePaths = [
|
||||||
|
# still needs these paths despite it using the portal's file-chooser :?
|
||||||
|
"Pictures/cat"
|
||||||
|
"Pictures/Screenshots"
|
||||||
|
"Pictures/servo-macros"
|
||||||
|
"Videos/local"
|
||||||
|
"Videos/servo"
|
||||||
|
"tmp"
|
||||||
|
];
|
||||||
|
|
||||||
dtc.sandbox.method = "bwrap";
|
dtc.sandbox.method = "bwrap";
|
||||||
dtc.sandbox.autodetectCliPaths = true; # TODO:sandbox: untested
|
dtc.sandbox.autodetectCliPaths = true; # TODO:sandbox: untested
|
||||||
|
|
||||||
dtrx.sandbox.method = "bwrap";
|
dtrx.sandbox.method = "bwrap";
|
||||||
dtrx.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
dtrx.sandbox.whitelistPwd = true;
|
dtrx.sandbox.whitelistPwd = true;
|
||||||
dtrx.sandbox.autodetectCliPaths = "existing"; #< for the archive
|
dtrx.sandbox.autodetectCliPaths = "existing"; #< for the archive
|
||||||
|
|
||||||
|
duplicity = {};
|
||||||
|
|
||||||
e2fsprogs.sandbox.method = "landlock";
|
e2fsprogs.sandbox.method = "landlock";
|
||||||
e2fsprogs.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
e2fsprogs.sandbox.autodetectCliPaths = "existing";
|
e2fsprogs.sandbox.autodetectCliPaths = "existing";
|
||||||
|
|
||||||
efibootmgr.sandbox.method = "landlock";
|
efibootmgr.sandbox.method = "landlock";
|
||||||
efibootmgr.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
efibootmgr.sandbox.extraPaths = [
|
efibootmgr.sandbox.extraPaths = [
|
||||||
"/sys/firmware/efi"
|
"/sys/firmware/efi"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
eg25-control = {};
|
||||||
|
|
||||||
electrum.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
electrum.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
electrum.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
electrum.sandbox.net = "all"; # TODO: probably want to make this run behind a VPN, always
|
electrum.sandbox.net = "all"; # TODO: probably want to make this run behind a VPN, always
|
||||||
electrum.sandbox.whitelistWayland = true;
|
electrum.sandbox.whitelistWayland = true;
|
||||||
electrum.persist.byStore.cryptClearOnBoot = [ ".electrum" ]; #< TODO: use XDG dirs!
|
electrum.persist.byStore.cryptClearOnBoot = [ ".electrum" ]; #< TODO: use XDG dirs!
|
||||||
|
|
||||||
endless-sky.persist.byStore.plaintext = [ ".local/share/endless-sky" ];
|
endless-sky.persist.byStore.plaintext = [ ".local/share/endless-sky" ];
|
||||||
endless-sky.sandbox.method = "bwrap";
|
endless-sky.sandbox.method = "bwrap";
|
||||||
endless-sky.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
endless-sky.sandbox.whitelistAudio = true;
|
endless-sky.sandbox.whitelistAudio = true;
|
||||||
endless-sky.sandbox.whitelistDri = true;
|
endless-sky.sandbox.whitelistDri = true;
|
||||||
endless-sky.sandbox.whitelistWayland = true;
|
endless-sky.sandbox.whitelistWayland = true;
|
||||||
@@ -322,14 +331,12 @@ in
|
|||||||
emote.persist.byStore.plaintext = [ ".local/share/Emote" ];
|
emote.persist.byStore.plaintext = [ ".local/share/Emote" ];
|
||||||
|
|
||||||
ethtool.sandbox.method = "landlock";
|
ethtool.sandbox.method = "landlock";
|
||||||
ethtool.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
ethtool.sandbox.capabilities = [ "net_admin" ];
|
ethtool.sandbox.capabilities = [ "net_admin" ];
|
||||||
|
|
||||||
# eza `ls` replacement
|
# eza `ls` replacement
|
||||||
# landlock is OK, only `whitelistPwd` doesn't make the intermediate symlinks traversable, so it breaks on e.g. ~/Videos/servo/Shows/foo
|
# landlock is OK, only `whitelistPwd` doesn't make the intermediate symlinks traversable, so it breaks on e.g. ~/Videos/servo/Shows/foo
|
||||||
# eza.sandbox.method = "landlock";
|
# eza.sandbox.method = "landlock";
|
||||||
eza.sandbox.method = "bwrap";
|
eza.sandbox.method = "bwrap";
|
||||||
eza.sandbox.wrapperType = "wrappedDerivation"; # slow to build
|
|
||||||
eza.sandbox.autodetectCliPaths = true;
|
eza.sandbox.autodetectCliPaths = true;
|
||||||
eza.sandbox.whitelistPwd = true;
|
eza.sandbox.whitelistPwd = true;
|
||||||
eza.sandbox.extraHomePaths = [
|
eza.sandbox.extraHomePaths = [
|
||||||
@@ -339,11 +346,9 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
fatresize.sandbox.method = "landlock";
|
fatresize.sandbox.method = "landlock";
|
||||||
fatresize.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
fatresize.sandbox.autodetectCliPaths = "parent"; # /dev/sda1 -> needs /dev/sda
|
fatresize.sandbox.autodetectCliPaths = "parent"; # /dev/sda1 -> needs /dev/sda
|
||||||
|
|
||||||
fd.sandbox.method = "landlock";
|
fd.sandbox.method = "landlock";
|
||||||
fd.sandbox.wrapperType = "wrappedDerivation"; # slow to build
|
|
||||||
fd.sandbox.autodetectCliPaths = true;
|
fd.sandbox.autodetectCliPaths = true;
|
||||||
fd.sandbox.whitelistPwd = true;
|
fd.sandbox.whitelistPwd = true;
|
||||||
fd.sandbox.extraHomePaths = [
|
fd.sandbox.extraHomePaths = [
|
||||||
@@ -353,15 +358,12 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
ffmpeg.sandbox.method = "bwrap";
|
ffmpeg.sandbox.method = "bwrap";
|
||||||
ffmpeg.sandbox.wrapperType = "wrappedDerivation"; # slow to build
|
|
||||||
ffmpeg.sandbox.autodetectCliPaths = "existingFileOrParent"; # it outputs uncreated files -> parent dir needs mounting
|
ffmpeg.sandbox.autodetectCliPaths = "existingFileOrParent"; # it outputs uncreated files -> parent dir needs mounting
|
||||||
|
|
||||||
file.sandbox.method = "bwrap";
|
file.sandbox.method = "bwrap";
|
||||||
file.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
file.sandbox.autodetectCliPaths = true;
|
file.sandbox.autodetectCliPaths = true;
|
||||||
|
|
||||||
findutils.sandbox.method = "bwrap";
|
findutils.sandbox.method = "bwrap";
|
||||||
findutils.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
findutils.sandbox.autodetectCliPaths = true;
|
findutils.sandbox.autodetectCliPaths = true;
|
||||||
findutils.sandbox.whitelistPwd = true;
|
findutils.sandbox.whitelistPwd = true;
|
||||||
findutils.sandbox.extraHomePaths = [
|
findutils.sandbox.extraHomePaths = [
|
||||||
@@ -373,14 +375,12 @@ in
|
|||||||
fluffychat-moby.persist.byStore.plaintext = [ ".local/share/chat.fluffy.fluffychat" ];
|
fluffychat-moby.persist.byStore.plaintext = [ ".local/share/chat.fluffy.fluffychat" ];
|
||||||
|
|
||||||
font-manager.sandbox.method = "bwrap";
|
font-manager.sandbox.method = "bwrap";
|
||||||
font-manager.sandbox.wrapperType = "inplace"; # .desktop and dbus .service file refer to /libexec
|
font-manager.packageUnwrapped = pkgs.rmDbusServicesInPlace (pkgs.font-manager.override {
|
||||||
font-manager.packageUnwrapped = pkgs.font-manager.override {
|
|
||||||
# build without the "Google Fonts" integration feature, to save closure / avoid webkitgtk_4_0
|
# build without the "Google Fonts" integration feature, to save closure / avoid webkitgtk_4_0
|
||||||
withWebkit = false;
|
withWebkit = false;
|
||||||
};
|
});
|
||||||
|
|
||||||
forkstat.sandbox.method = "landlock"; #< doesn't seem to support bwrap
|
forkstat.sandbox.method = "landlock"; #< doesn't seem to support bwrap
|
||||||
forkstat.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
forkstat.sandbox.extraConfig = [
|
forkstat.sandbox.extraConfig = [
|
||||||
"--sane-sandbox-keep-namespace" "pid"
|
"--sane-sandbox-keep-namespace" "pid"
|
||||||
];
|
];
|
||||||
@@ -388,12 +388,7 @@ in
|
|||||||
"/proc"
|
"/proc"
|
||||||
];
|
];
|
||||||
|
|
||||||
# fuzzel: TODO: re-enable sandbox. i use fuzzel both as an entry system (snippets) AND an app-launcher.
|
|
||||||
# as an app-launcher, it cannot be sandboxed without over-restricting the app it launches.
|
|
||||||
# should probably make it not be an app-launcher
|
|
||||||
fuzzel.sandbox.enable = false;
|
|
||||||
fuzzel.sandbox.method = "bwrap"; #< landlock nearly works, but unable to open ~/.cache
|
fuzzel.sandbox.method = "bwrap"; #< landlock nearly works, but unable to open ~/.cache
|
||||||
fuzzel.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
fuzzel.sandbox.whitelistWayland = true;
|
fuzzel.sandbox.whitelistWayland = true;
|
||||||
fuzzel.persist.byStore.private = [
|
fuzzel.persist.byStore.private = [
|
||||||
# this is a file of recent selections
|
# this is a file of recent selections
|
||||||
@@ -401,20 +396,21 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
gawk.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
gawk.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
gawk.sandbox.wrapperType = "inplace"; # share/gawk libraries refer to /libexec
|
gawk.sandbox.wrapperType = "inplace"; # /share/gawk libraries refer to /libexec
|
||||||
gawk.sandbox.autodetectCliPaths = true;
|
gawk.sandbox.autodetectCliPaths = true;
|
||||||
|
|
||||||
gdb.sandbox.enable = false; # gdb doesn't sandbox well. i don't know how you could.
|
gdb.sandbox.enable = false; # gdb doesn't sandbox well. i don't know how you could.
|
||||||
# gdb.sandbox.method = "landlock"; # permission denied when trying to attach, even as root
|
# gdb.sandbox.method = "landlock"; # permission denied when trying to attach, even as root
|
||||||
gdb.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
gdb.sandbox.autodetectCliPaths = true;
|
gdb.sandbox.autodetectCliPaths = true;
|
||||||
|
|
||||||
|
geoclue2-with-demo-agent = {};
|
||||||
|
|
||||||
# MS GitHub stores auth token in .config
|
# MS GitHub stores auth token in .config
|
||||||
# TODO: we can populate gh's stuff statically; it even lets us use the same oauth across machines
|
# TODO: we can populate gh's stuff statically; it even lets us use the same oauth across machines
|
||||||
gh.persist.byStore.private = [ ".config/gh" ];
|
gh.persist.byStore.private = [ ".config/gh" ];
|
||||||
|
|
||||||
gimp.sandbox.method = "bwrap";
|
gimp.sandbox.method = "bwrap";
|
||||||
gimp.sandbox.wrapperType = "wrappedDerivation";
|
gimp.sandbox.whitelistX = true;
|
||||||
gimp.sandbox.whitelistWayland = true;
|
gimp.sandbox.whitelistWayland = true;
|
||||||
gimp.sandbox.extraHomePaths = [
|
gimp.sandbox.extraHomePaths = [
|
||||||
"Pictures/albums"
|
"Pictures/albums"
|
||||||
@@ -433,39 +429,32 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
"gnome.gnome-calculator".sandbox.method = "bwrap";
|
"gnome.gnome-calculator".sandbox.method = "bwrap";
|
||||||
"gnome.gnome-calculator".sandbox.wrapperType = "inplace"; # /libexec/gnome-calculator-search-provider
|
|
||||||
"gnome.gnome-calculator".sandbox.whitelistWayland = true;
|
"gnome.gnome-calculator".sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
# gnome-calendar surely has data to persist, but i use it strictly to do date math, not track events.
|
# gnome-calendar surely has data to persist, but i use it strictly to do date math, not track events.
|
||||||
"gnome.gnome-calendar".sandbox.method = "bwrap";
|
"gnome.gnome-calendar".sandbox.method = "bwrap";
|
||||||
"gnome.gnome-calendar".sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
"gnome.gnome-calendar".sandbox.whitelistWayland = true;
|
"gnome.gnome-calendar".sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
"gnome.gnome-clocks".sandbox.method = "bwrap";
|
"gnome.gnome-clocks".sandbox.method = "bwrap";
|
||||||
"gnome.gnome-clocks".sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
"gnome.gnome-clocks".sandbox.whitelistWayland = true;
|
"gnome.gnome-clocks".sandbox.whitelistWayland = true;
|
||||||
"gnome.gnome-clocks".suggestedPrograms = [ "dconf" ];
|
"gnome.gnome-clocks".suggestedPrograms = [ "dconf" ];
|
||||||
|
|
||||||
# gnome-disks
|
# gnome-disks
|
||||||
"gnome.gnome-disk-utility".sandbox.method = "bwrap";
|
"gnome.gnome-disk-utility".sandbox.method = "bwrap";
|
||||||
"gnome.gnome-disk-utility".sandbox.wrapperType = "inplace"; # /etc/xdg/autostart
|
|
||||||
"gnome.gnome-disk-utility".sandbox.whitelistDbus = [ "system" ];
|
"gnome.gnome-disk-utility".sandbox.whitelistDbus = [ "system" ];
|
||||||
"gnome.gnome-disk-utility".sandbox.whitelistWayland = true;
|
"gnome.gnome-disk-utility".sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
# seahorse: dump gnome-keyring secrets.
|
# seahorse: dump gnome-keyring secrets.
|
||||||
# N.B.: it can also manage ~/.ssh keys, but i explicitly don't add those to the sandbox for now.
|
# N.B.: it can also manage ~/.ssh keys, but i explicitly don't add those to the sandbox for now.
|
||||||
"gnome.seahorse".sandbox.method = "bwrap";
|
"gnome.seahorse".sandbox.method = "bwrap";
|
||||||
"gnome.seahorse".sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
"gnome.seahorse".sandbox.whitelistDbus = [ "user" ];
|
"gnome.seahorse".sandbox.whitelistDbus = [ "user" ];
|
||||||
"gnome.seahorse".sandbox.whitelistWayland = true;
|
"gnome.seahorse".sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
gnome-2048.sandbox.method = "bwrap";
|
gnome-2048.sandbox.method = "bwrap";
|
||||||
gnome-2048.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
gnome-2048.sandbox.whitelistWayland = true;
|
gnome-2048.sandbox.whitelistWayland = true;
|
||||||
gnome-2048.persist.byStore.plaintext = [ ".local/share/gnome-2048/scores" ];
|
gnome-2048.persist.byStore.plaintext = [ ".local/share/gnome-2048/scores" ];
|
||||||
|
|
||||||
gnome-frog.sandbox.method = "bwrap";
|
gnome-frog.sandbox.method = "bwrap";
|
||||||
gnome-frog.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
gnome-frog.sandbox.whitelistWayland = true;
|
gnome-frog.sandbox.whitelistWayland = true;
|
||||||
gnome-frog.sandbox.whitelistDbus = [ "user" ];
|
gnome-frog.sandbox.whitelistDbus = [ "user" ];
|
||||||
gnome-frog.sandbox.extraPaths = [
|
gnome-frog.sandbox.extraPaths = [
|
||||||
@@ -492,11 +481,9 @@ in
|
|||||||
# 2. no two shaded tiles can be direct N/S/E/W neighbors
|
# 2. no two shaded tiles can be direct N/S/E/W neighbors
|
||||||
# - win once (1) and (2) are satisfied
|
# - win once (1) and (2) are satisfied
|
||||||
"gnome.hitori".sandbox.method = "bwrap";
|
"gnome.hitori".sandbox.method = "bwrap";
|
||||||
"gnome.hitori".sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
"gnome.hitori".sandbox.whitelistWayland = true;
|
"gnome.hitori".sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
gnugrep.sandbox.method = "bwrap";
|
gnugrep.sandbox.method = "bwrap";
|
||||||
gnugrep.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
gnugrep.sandbox.autodetectCliPaths = true;
|
gnugrep.sandbox.autodetectCliPaths = true;
|
||||||
gnugrep.sandbox.whitelistPwd = true;
|
gnugrep.sandbox.whitelistPwd = true;
|
||||||
gnugrep.sandbox.extraHomePaths = [
|
gnugrep.sandbox.extraHomePaths = [
|
||||||
@@ -505,15 +492,24 @@ in
|
|||||||
".persist/plaintext"
|
".persist/plaintext"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# sed: there is an edgecase of `--file=<foo>`, wherein `foo` won't be whitelisted.
|
||||||
|
gnused.sandbox.method = "bwrap";
|
||||||
|
gnused.sandbox.autodetectCliPaths = "existingFile";
|
||||||
|
gnused.sandbox.whitelistPwd = true; #< `-i` flag creates a temporary file in pwd (?) and then moves it.
|
||||||
|
|
||||||
|
gpsd = {};
|
||||||
|
|
||||||
gptfdisk.sandbox.method = "landlock";
|
gptfdisk.sandbox.method = "landlock";
|
||||||
gptfdisk.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
gptfdisk.sandbox.extraPaths = [
|
gptfdisk.sandbox.extraPaths = [
|
||||||
"/dev"
|
"/dev"
|
||||||
];
|
];
|
||||||
gptfdisk.sandbox.autodetectCliPaths = "existing"; #< sometimes you'll use gdisk on a device file.
|
gptfdisk.sandbox.autodetectCliPaths = "existing"; #< sometimes you'll use gdisk on a device file.
|
||||||
|
|
||||||
|
grim.sandbox.method = "bwrap";
|
||||||
|
grim.sandbox.autodetectCliPaths = "existingOrParent";
|
||||||
|
grim.sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
hase.sandbox.method = "bwrap";
|
hase.sandbox.method = "bwrap";
|
||||||
hase.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
hase.sandbox.net = "clearnet";
|
hase.sandbox.net = "clearnet";
|
||||||
hase.sandbox.whitelistAudio = true;
|
hase.sandbox.whitelistAudio = true;
|
||||||
hase.sandbox.whitelistDri = true;
|
hase.sandbox.whitelistDri = true;
|
||||||
@@ -521,35 +517,19 @@ in
|
|||||||
|
|
||||||
# hdparm: has to be run as sudo. e.g. `sudo hdparm -i /dev/sda`
|
# hdparm: has to be run as sudo. e.g. `sudo hdparm -i /dev/sda`
|
||||||
hdparm.sandbox.method = "bwrap";
|
hdparm.sandbox.method = "bwrap";
|
||||||
hdparm.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
hdparm.sandbox.autodetectCliPaths = true;
|
hdparm.sandbox.autodetectCliPaths = true;
|
||||||
|
|
||||||
host.sandbox.method = "landlock";
|
host.sandbox.method = "landlock";
|
||||||
host.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
host.sandbox.net = "all"; #< technically, only needs to contact localhost's DNS server
|
host.sandbox.net = "all"; #< technically, only needs to contact localhost's DNS server
|
||||||
|
|
||||||
htop.sandbox.method = "landlock";
|
|
||||||
htop.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
htop.sandbox.extraPaths = [
|
|
||||||
"/proc"
|
|
||||||
"/sys/devices"
|
|
||||||
];
|
|
||||||
htop.persist.byStore.plaintext = [
|
|
||||||
# consider setting `show_program_path=0` and either `hide_userland_threads=1` or `show_thread_names=1`
|
|
||||||
".config/htop"
|
|
||||||
];
|
|
||||||
|
|
||||||
iftop.sandbox.method = "landlock";
|
iftop.sandbox.method = "landlock";
|
||||||
iftop.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
iftop.sandbox.capabilities = [ "net_raw" ];
|
iftop.sandbox.capabilities = [ "net_raw" ];
|
||||||
|
|
||||||
# inetutils: ping, ifconfig, hostname, traceroute, whois, ....
|
# inetutils: ping, ifconfig, hostname, traceroute, whois, ....
|
||||||
# N.B.: inetutils' `ping` is shadowed by iputils' ping (by nixos, intentionally).
|
# N.B.: inetutils' `ping` is shadowed by iputils' ping (by nixos, intentionally).
|
||||||
inetutils.sandbox.method = "landlock"; # want to keep the same netns, at least.
|
inetutils.sandbox.method = "landlock"; # want to keep the same netns, at least.
|
||||||
inetutils.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
|
|
||||||
inkscape.sandbox.method = "bwrap";
|
inkscape.sandbox.method = "bwrap";
|
||||||
inkscape.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
inkscape.sandbox.whitelistWayland = true;
|
inkscape.sandbox.whitelistWayland = true;
|
||||||
inkscape.sandbox.extraHomePaths = [
|
inkscape.sandbox.extraHomePaths = [
|
||||||
"Pictures/albums"
|
"Pictures/albums"
|
||||||
@@ -565,7 +545,6 @@ in
|
|||||||
inkscape.sandbox.autodetectCliPaths = true;
|
inkscape.sandbox.autodetectCliPaths = true;
|
||||||
|
|
||||||
iotop.sandbox.method = "landlock";
|
iotop.sandbox.method = "landlock";
|
||||||
iotop.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
iotop.sandbox.extraPaths = [
|
iotop.sandbox.extraPaths = [
|
||||||
"/proc"
|
"/proc"
|
||||||
];
|
];
|
||||||
@@ -573,38 +552,35 @@ in
|
|||||||
|
|
||||||
# provides `ip`, `routel`, others
|
# provides `ip`, `routel`, others
|
||||||
iproute2.sandbox.method = "landlock";
|
iproute2.sandbox.method = "landlock";
|
||||||
iproute2.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
iproute2.sandbox.net = "all";
|
iproute2.sandbox.net = "all";
|
||||||
iproute2.sandbox.capabilities = [ "net_admin" ];
|
iproute2.sandbox.capabilities = [ "net_admin" ];
|
||||||
|
iproute2.sandbox.extraPaths = [
|
||||||
|
"/run/netns" # for `ip netns ...` to work
|
||||||
|
"/var/run/netns"
|
||||||
|
];
|
||||||
|
|
||||||
iptables.sandbox.method = "landlock";
|
iptables.sandbox.method = "landlock";
|
||||||
iptables.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
iptables.sandbox.net = "all";
|
iptables.sandbox.net = "all";
|
||||||
iptables.sandbox.capabilities = [ "net_admin" ];
|
iptables.sandbox.capabilities = [ "net_admin" ];
|
||||||
|
|
||||||
# iputils provides `ping` (and arping, clockdiff, tracepath)
|
# iputils provides `ping` (and arping, clockdiff, tracepath)
|
||||||
iputils.sandbox.method = "landlock";
|
iputils.sandbox.method = "landlock";
|
||||||
iputils.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
iputils.sandbox.net = "all";
|
iputils.sandbox.net = "all";
|
||||||
iputils.sandbox.capabilities = [ "net_raw" ];
|
iputils.sandbox.capabilities = [ "net_raw" ];
|
||||||
|
|
||||||
iw.sandbox.method = "landlock";
|
iw.sandbox.method = "landlock";
|
||||||
iw.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
iw.sandbox.net = "all";
|
iw.sandbox.net = "all";
|
||||||
iw.sandbox.capabilities = [ "net_admin" ];
|
iw.sandbox.capabilities = [ "net_admin" ];
|
||||||
|
|
||||||
jq.sandbox.method = "bwrap";
|
jq.sandbox.method = "bwrap";
|
||||||
jq.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
jq.sandbox.autodetectCliPaths = "existingFile";
|
jq.sandbox.autodetectCliPaths = "existingFile";
|
||||||
|
|
||||||
killall.sandbox.method = "landlock";
|
killall.sandbox.method = "landlock";
|
||||||
killall.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
killall.sandbox.extraPaths = [
|
killall.sandbox.extraPaths = [
|
||||||
"/proc"
|
"/proc"
|
||||||
];
|
];
|
||||||
|
|
||||||
krita.sandbox.method = "bwrap";
|
krita.sandbox.method = "bwrap";
|
||||||
krita.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
krita.sandbox.whitelistWayland = true;
|
krita.sandbox.whitelistWayland = true;
|
||||||
krita.sandbox.autodetectCliPaths = "existing";
|
krita.sandbox.autodetectCliPaths = "existing";
|
||||||
krita.sandbox.extraHomePaths = [
|
krita.sandbox.extraHomePaths = [
|
||||||
@@ -622,11 +598,9 @@ in
|
|||||||
libcap_ng.sandbox.enable = false; # there's something about /proc/$pid/fd which breaks `readlink`/stat with every sandbox technique (except capsh-only)
|
libcap_ng.sandbox.enable = false; # there's something about /proc/$pid/fd which breaks `readlink`/stat with every sandbox technique (except capsh-only)
|
||||||
|
|
||||||
libnotify.sandbox.method = "bwrap";
|
libnotify.sandbox.method = "bwrap";
|
||||||
libnotify.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
libnotify.sandbox.whitelistDbus = [ "user" ]; # notify-send
|
libnotify.sandbox.whitelistDbus = [ "user" ]; # notify-send
|
||||||
|
|
||||||
losslesscut-bin.sandbox.method = "bwrap";
|
losslesscut-bin.sandbox.method = "bwrap";
|
||||||
losslesscut-bin.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
losslesscut-bin.sandbox.extraHomePaths = [
|
losslesscut-bin.sandbox.extraHomePaths = [
|
||||||
"Music"
|
"Music"
|
||||||
"Pictures/from" # videos from e.g. mobile phone
|
"Pictures/from" # videos from e.g. mobile phone
|
||||||
@@ -641,23 +615,10 @@ in
|
|||||||
losslesscut-bin.sandbox.whitelistX = true;
|
losslesscut-bin.sandbox.whitelistX = true;
|
||||||
|
|
||||||
lsof.sandbox.method = "capshonly"; # lsof doesn't sandbox under bwrap or even landlock w/ full access to /
|
lsof.sandbox.method = "capshonly"; # lsof doesn't sandbox under bwrap or even landlock w/ full access to /
|
||||||
lsof.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
|
|
||||||
"mate.engrampa".sandbox.method = "bwrap"; # TODO:sandbox: untested
|
lua = {};
|
||||||
"mate.engrampa".sandbox.wrapperType = "inplace";
|
|
||||||
"mate.engrampa".sandbox.whitelistWayland = true;
|
|
||||||
"mate.engrampa".sandbox.autodetectCliPaths = "existingOrParent";
|
|
||||||
"mate.engrampa".sandbox.extraHomePaths = [
|
|
||||||
"archive"
|
|
||||||
"Books/local"
|
|
||||||
"Books/servo"
|
|
||||||
"records"
|
|
||||||
"ref"
|
|
||||||
"tmp"
|
|
||||||
];
|
|
||||||
|
|
||||||
mercurial.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
mercurial.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
mercurial.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
mercurial.sandbox.net = "clearnet";
|
mercurial.sandbox.net = "clearnet";
|
||||||
mercurial.sandbox.whitelistPwd = true;
|
mercurial.sandbox.whitelistPwd = true;
|
||||||
|
|
||||||
@@ -665,7 +626,6 @@ in
|
|||||||
# XXX: is it really safe to persist this? it doesn't have info that could de-anonymize if captured?
|
# XXX: is it really safe to persist this? it doesn't have info that could de-anonymize if captured?
|
||||||
monero-gui.persist.byStore.plaintext = [ ".bitmonero" ];
|
monero-gui.persist.byStore.plaintext = [ ".bitmonero" ];
|
||||||
monero-gui.sandbox.method = "bwrap";
|
monero-gui.sandbox.method = "bwrap";
|
||||||
monero-gui.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
monero-gui.sandbox.net = "all";
|
monero-gui.sandbox.net = "all";
|
||||||
monero-gui.sandbox.extraHomePaths = [
|
monero-gui.sandbox.extraHomePaths = [
|
||||||
"records/finance/cryptocurrencies/monero"
|
"records/finance/cryptocurrencies/monero"
|
||||||
@@ -674,20 +634,16 @@ in
|
|||||||
mumble.persist.byStore.private = [ ".local/share/Mumble" ];
|
mumble.persist.byStore.private = [ ".local/share/Mumble" ];
|
||||||
|
|
||||||
nano.sandbox.method = "bwrap";
|
nano.sandbox.method = "bwrap";
|
||||||
nano.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
nano.sandbox.autodetectCliPaths = "existingFileOrParent";
|
nano.sandbox.autodetectCliPaths = "existingFileOrParent";
|
||||||
|
|
||||||
netcat.sandbox.method = "landlock";
|
netcat.sandbox.method = "landlock";
|
||||||
netcat.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
netcat.sandbox.net = "all";
|
netcat.sandbox.net = "all";
|
||||||
|
|
||||||
nethogs.sandbox.method = "capshonly"; # *partially* works under landlock w/ full access to /
|
nethogs.sandbox.method = "capshonly"; # *partially* works under landlock w/ full access to /
|
||||||
nethogs.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
nethogs.sandbox.capabilities = [ "net_admin" "net_raw" ];
|
nethogs.sandbox.capabilities = [ "net_admin" "net_raw" ];
|
||||||
|
|
||||||
# provides `arp`, `hostname`, `route`, `ifconfig`
|
# provides `arp`, `hostname`, `route`, `ifconfig`
|
||||||
nettools.sandbox.method = "landlock";
|
nettools.sandbox.method = "landlock";
|
||||||
nettools.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
nettools.sandbox.net = "all";
|
nettools.sandbox.net = "all";
|
||||||
nettools.sandbox.capabilities = [ "net_admin" "net_raw" ];
|
nettools.sandbox.capabilities = [ "net_admin" "net_raw" ];
|
||||||
nettools.sandbox.extraPaths = [
|
nettools.sandbox.extraPaths = [
|
||||||
@@ -695,7 +651,6 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
networkmanagerapplet.sandbox.method = "bwrap";
|
networkmanagerapplet.sandbox.method = "bwrap";
|
||||||
networkmanagerapplet.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
networkmanagerapplet.sandbox.whitelistWayland = true;
|
networkmanagerapplet.sandbox.whitelistWayland = true;
|
||||||
networkmanagerapplet.sandbox.whitelistDbus = [ "system" ];
|
networkmanagerapplet.sandbox.whitelistDbus = [ "system" ];
|
||||||
|
|
||||||
@@ -708,18 +663,17 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
nmap.sandbox.method = "bwrap";
|
nmap.sandbox.method = "bwrap";
|
||||||
nmap.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
nmap.sandbox.net = "all"; # clearnet and lan
|
nmap.sandbox.net = "all"; # clearnet and lan
|
||||||
|
|
||||||
nmon.sandbox.method = "landlock";
|
nmon.sandbox.method = "landlock";
|
||||||
nmon.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
nmon.sandbox.extraPaths = [
|
nmon.sandbox.extraPaths = [
|
||||||
"/proc"
|
"/proc"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
nodejs = {};
|
||||||
|
|
||||||
# `nvme list` only shows results when run as root.
|
# `nvme list` only shows results when run as root.
|
||||||
nvme-cli.sandbox.method = "landlock";
|
nvme-cli.sandbox.method = "landlock";
|
||||||
nvme-cli.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
nvme-cli.sandbox.extraPaths = [
|
nvme-cli.sandbox.extraPaths = [
|
||||||
"/sys/devices"
|
"/sys/devices"
|
||||||
"/sys/class/nvme"
|
"/sys/class/nvme"
|
||||||
@@ -731,25 +685,23 @@ in
|
|||||||
|
|
||||||
# contains only `oathtool`, which i only use for evaluating TOTP codes from CLI/stdin
|
# contains only `oathtool`, which i only use for evaluating TOTP codes from CLI/stdin
|
||||||
oath-toolkit.sandbox.method = "bwrap";
|
oath-toolkit.sandbox.method = "bwrap";
|
||||||
oath-toolkit.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
|
|
||||||
# settings (electron app)
|
# settings (electron app)
|
||||||
obsidian.persist.byStore.plaintext = [ ".config/obsidian" ];
|
obsidian.persist.byStore.plaintext = [ ".config/obsidian" ];
|
||||||
|
|
||||||
parted.sandbox.method = "landlock";
|
parted.sandbox.method = "landlock";
|
||||||
parted.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
parted.sandbox.extraPaths = [
|
parted.sandbox.extraPaths = [
|
||||||
"/dev"
|
"/dev"
|
||||||
];
|
];
|
||||||
parted.sandbox.autodetectCliPaths = "existing"; #< sometimes you'll use parted on a device file.
|
parted.sandbox.autodetectCliPaths = "existing"; #< sometimes you'll use parted on a device file.
|
||||||
|
|
||||||
|
patchelf = {};
|
||||||
|
|
||||||
pavucontrol.sandbox.method = "bwrap";
|
pavucontrol.sandbox.method = "bwrap";
|
||||||
pavucontrol.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
pavucontrol.sandbox.whitelistAudio = true;
|
pavucontrol.sandbox.whitelistAudio = true;
|
||||||
pavucontrol.sandbox.whitelistWayland = true;
|
pavucontrol.sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
pciutils.sandbox.method = "landlock";
|
pciutils.sandbox.method = "landlock";
|
||||||
pciutils.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
pciutils.sandbox.extraPaths = [
|
pciutils.sandbox.extraPaths = [
|
||||||
"/sys/bus/pci"
|
"/sys/bus/pci"
|
||||||
"/sys/devices"
|
"/sys/devices"
|
||||||
@@ -758,7 +710,6 @@ in
|
|||||||
"perlPackages.FileMimeInfo".sandbox.enable = false; #< TODO: sandbox `mimetype` but not `mimeopen`.
|
"perlPackages.FileMimeInfo".sandbox.enable = false; #< TODO: sandbox `mimetype` but not `mimeopen`.
|
||||||
|
|
||||||
powertop.sandbox.method = "landlock";
|
powertop.sandbox.method = "landlock";
|
||||||
powertop.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
powertop.sandbox.capabilities = [ "ipc_lock" "sys_admin" ];
|
powertop.sandbox.capabilities = [ "ipc_lock" "sys_admin" ];
|
||||||
powertop.sandbox.extraPaths = [
|
powertop.sandbox.extraPaths = [
|
||||||
"/proc"
|
"/proc"
|
||||||
@@ -767,18 +718,23 @@ in
|
|||||||
"/sys/kernel"
|
"/sys/kernel"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# procps: free, pgrep, pidof, pkill, ps, pwait, top, uptime, couple others
|
||||||
|
procps.sandbox.method = "bwrap";
|
||||||
|
procps.sandbox.extraConfig = [
|
||||||
|
"--sane-sandbox-keep-namespace" "pid"
|
||||||
|
];
|
||||||
|
|
||||||
pstree.sandbox.method = "landlock";
|
pstree.sandbox.method = "landlock";
|
||||||
pstree.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
pstree.sandbox.extraPaths = [
|
pstree.sandbox.extraPaths = [
|
||||||
"/proc"
|
"/proc"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pulseaudio = {};
|
||||||
|
|
||||||
pulsemixer.sandbox.method = "landlock";
|
pulsemixer.sandbox.method = "landlock";
|
||||||
pulsemixer.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
pulsemixer.sandbox.whitelistAudio = true;
|
pulsemixer.sandbox.whitelistAudio = true;
|
||||||
|
|
||||||
pwvucontrol.sandbox.method = "bwrap";
|
pwvucontrol.sandbox.method = "bwrap";
|
||||||
pwvucontrol.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
pwvucontrol.sandbox.whitelistAudio = true;
|
pwvucontrol.sandbox.whitelistAudio = true;
|
||||||
pwvucontrol.sandbox.whitelistWayland = true;
|
pwvucontrol.sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
@@ -786,7 +742,6 @@ in
|
|||||||
requests
|
requests
|
||||||
]);
|
]);
|
||||||
python3-repl.sandbox.method = "bwrap";
|
python3-repl.sandbox.method = "bwrap";
|
||||||
python3-repl.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
python3-repl.sandbox.net = "clearnet";
|
python3-repl.sandbox.net = "clearnet";
|
||||||
python3-repl.sandbox.extraHomePaths = [
|
python3-repl.sandbox.extraHomePaths = [
|
||||||
"/"
|
"/"
|
||||||
@@ -797,20 +752,24 @@ in
|
|||||||
qemu.slowToBuild = true;
|
qemu.slowToBuild = true;
|
||||||
|
|
||||||
rsync.sandbox.method = "bwrap";
|
rsync.sandbox.method = "bwrap";
|
||||||
rsync.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
rsync.sandbox.net = "clearnet";
|
rsync.sandbox.net = "clearnet";
|
||||||
rsync.sandbox.autodetectCliPaths = "existingOrParent";
|
rsync.sandbox.autodetectCliPaths = "existingOrParent";
|
||||||
|
|
||||||
|
rustc = {};
|
||||||
|
|
||||||
|
sane-open-desktop.sandbox.enable = false; #< trivial script, and all our deps are sandboxed
|
||||||
|
sane-open-desktop.suggestedPrograms = [
|
||||||
|
"gdbus"
|
||||||
|
];
|
||||||
|
|
||||||
screen.sandbox.enable = false; #< tty; needs to run anything
|
screen.sandbox.enable = false; #< tty; needs to run anything
|
||||||
|
|
||||||
sequoia.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
sequoia.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
sequoia.sandbox.wrapperType = "wrappedDerivation"; # slow to build
|
|
||||||
sequoia.sandbox.whitelistPwd = true;
|
sequoia.sandbox.whitelistPwd = true;
|
||||||
sequoia.sandbox.autodetectCliPaths = true;
|
sequoia.sandbox.autodetectCliPaths = true;
|
||||||
|
|
||||||
shattered-pixel-dungeon.persist.byStore.plaintext = [ ".local/share/.shatteredpixel/shattered-pixel-dungeon" ];
|
shattered-pixel-dungeon.persist.byStore.plaintext = [ ".local/share/.shatteredpixel/shattered-pixel-dungeon" ];
|
||||||
shattered-pixel-dungeon.sandbox.method = "bwrap";
|
shattered-pixel-dungeon.sandbox.method = "bwrap";
|
||||||
shattered-pixel-dungeon.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
shattered-pixel-dungeon.sandbox.whitelistAudio = true;
|
shattered-pixel-dungeon.sandbox.whitelistAudio = true;
|
||||||
shattered-pixel-dungeon.sandbox.whitelistDri = true;
|
shattered-pixel-dungeon.sandbox.whitelistDri = true;
|
||||||
shattered-pixel-dungeon.sandbox.whitelistWayland = true;
|
shattered-pixel-dungeon.sandbox.whitelistWayland = true;
|
||||||
@@ -818,6 +777,9 @@ in
|
|||||||
# printer/filament settings
|
# printer/filament settings
|
||||||
slic3r.persist.byStore.plaintext = [ ".Slic3r" ];
|
slic3r.persist.byStore.plaintext = [ ".Slic3r" ];
|
||||||
|
|
||||||
|
slurp.sandbox.method = "bwrap";
|
||||||
|
slurp.sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
# use like `sudo smartctl /dev/sda -a`
|
# use like `sudo smartctl /dev/sda -a`
|
||||||
smartmontools.sandbox.method = "landlock";
|
smartmontools.sandbox.method = "landlock";
|
||||||
smartmontools.sandbox.wrapperType = "inplace"; # ships a script in /etc that calls into its bin
|
smartmontools.sandbox.wrapperType = "inplace"; # ships a script in /etc that calls into its bin
|
||||||
@@ -825,7 +787,6 @@ in
|
|||||||
smartmontools.sandbox.capabilities = [ "sys_rawio" ];
|
smartmontools.sandbox.capabilities = [ "sys_rawio" ];
|
||||||
|
|
||||||
sops.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
sops.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
sops.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sops.sandbox.extraHomePaths = [
|
sops.sandbox.extraHomePaths = [
|
||||||
".config/sops"
|
".config/sops"
|
||||||
"dev/nixos"
|
"dev/nixos"
|
||||||
@@ -835,7 +796,6 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
soundconverter.sandbox.method = "bwrap";
|
soundconverter.sandbox.method = "bwrap";
|
||||||
soundconverter.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
soundconverter.sandbox.whitelistWayland = true;
|
soundconverter.sandbox.whitelistWayland = true;
|
||||||
soundconverter.sandbox.extraHomePaths = [
|
soundconverter.sandbox.extraHomePaths = [
|
||||||
"Music"
|
"Music"
|
||||||
@@ -849,25 +809,25 @@ in
|
|||||||
soundconverter.sandbox.autodetectCliPaths = "existingOrParent";
|
soundconverter.sandbox.autodetectCliPaths = "existingOrParent";
|
||||||
|
|
||||||
sox.sandbox.method = "bwrap";
|
sox.sandbox.method = "bwrap";
|
||||||
sox.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sox.sandbox.autodetectCliPaths = "existingFileOrParent";
|
sox.sandbox.autodetectCliPaths = "existingFileOrParent";
|
||||||
sox.sandbox.whitelistAudio = true;
|
sox.sandbox.whitelistAudio = true;
|
||||||
|
|
||||||
space-cadet-pinball.persist.byStore.plaintext = [ ".local/share/SpaceCadetPinball" ];
|
space-cadet-pinball.persist.byStore.plaintext = [ ".local/share/SpaceCadetPinball" ];
|
||||||
space-cadet-pinball.sandbox.method = "bwrap";
|
space-cadet-pinball.sandbox.method = "bwrap";
|
||||||
space-cadet-pinball.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
space-cadet-pinball.sandbox.whitelistAudio = true;
|
space-cadet-pinball.sandbox.whitelistAudio = true;
|
||||||
space-cadet-pinball.sandbox.whitelistDri = true;
|
space-cadet-pinball.sandbox.whitelistDri = true;
|
||||||
space-cadet-pinball.sandbox.whitelistWayland = true;
|
space-cadet-pinball.sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
speedtest-cli.sandbox.method = "bwrap";
|
speedtest-cli.sandbox.method = "bwrap";
|
||||||
speedtest-cli.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
speedtest-cli.sandbox.net = "all";
|
speedtest-cli.sandbox.net = "all";
|
||||||
|
|
||||||
|
sqlite = {};
|
||||||
|
|
||||||
|
sshfs-fuse = {}; # used by fs.nix
|
||||||
|
|
||||||
strace.sandbox.enable = false; #< needs to `exec` its args, and therefore support *anything*
|
strace.sandbox.enable = false; #< needs to `exec` its args, and therefore support *anything*
|
||||||
|
|
||||||
subversion.sandbox.method = "bwrap";
|
subversion.sandbox.method = "bwrap";
|
||||||
subversion.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
subversion.sandbox.net = "clearnet";
|
subversion.sandbox.net = "clearnet";
|
||||||
subversion.sandbox.whitelistPwd = true;
|
subversion.sandbox.whitelistPwd = true;
|
||||||
sudo.sandbox.enable = false;
|
sudo.sandbox.enable = false;
|
||||||
@@ -879,8 +839,11 @@ in
|
|||||||
superTux.sandbox.whitelistWayland = true;
|
superTux.sandbox.whitelistWayland = true;
|
||||||
superTux.persist.byStore.plaintext = [ ".local/share/supertux2" ];
|
superTux.persist.byStore.plaintext = [ ".local/share/supertux2" ];
|
||||||
|
|
||||||
|
swappy.sandbox.method = "bwrap";
|
||||||
|
swappy.sandbox.autodetectCliPaths = "existingFileOrParent";
|
||||||
|
swappy.sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
tcpdump.sandbox.method = "landlock";
|
tcpdump.sandbox.method = "landlock";
|
||||||
tcpdump.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
tcpdump.sandbox.net = "all";
|
tcpdump.sandbox.net = "all";
|
||||||
tcpdump.sandbox.autodetectCliPaths = "existingFileOrParent";
|
tcpdump.sandbox.autodetectCliPaths = "existingFileOrParent";
|
||||||
tcpdump.sandbox.capabilities = [ "net_admin" "net_raw" ];
|
tcpdump.sandbox.capabilities = [ "net_admin" "net_raw" ];
|
||||||
@@ -890,12 +853,10 @@ in
|
|||||||
tokodon.persist.byStore.private = [ ".cache/KDE/tokodon" ];
|
tokodon.persist.byStore.private = [ ".cache/KDE/tokodon" ];
|
||||||
|
|
||||||
tree.sandbox.method = "landlock";
|
tree.sandbox.method = "landlock";
|
||||||
tree.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
tree.sandbox.autodetectCliPaths = true;
|
tree.sandbox.autodetectCliPaths = true;
|
||||||
tree.sandbox.whitelistPwd = true;
|
tree.sandbox.whitelistPwd = true;
|
||||||
|
|
||||||
tumiki-fighters.sandbox.method = "bwrap";
|
tumiki-fighters.sandbox.method = "bwrap";
|
||||||
tumiki-fighters.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
tumiki-fighters.sandbox.whitelistAudio = true;
|
tumiki-fighters.sandbox.whitelistAudio = true;
|
||||||
tumiki-fighters.sandbox.whitelistDri = true; #< not strictly necessary, but triples CPU perf
|
tumiki-fighters.sandbox.whitelistDri = true; #< not strictly necessary, but triples CPU perf
|
||||||
tumiki-fighters.sandbox.whitelistWayland = true;
|
tumiki-fighters.sandbox.whitelistWayland = true;
|
||||||
@@ -904,34 +865,28 @@ in
|
|||||||
util-linux.sandbox.enable = false; #< TODO: possible to sandbox if i specific a different profile for each of its ~50 binaries
|
util-linux.sandbox.enable = false; #< TODO: possible to sandbox if i specific a different profile for each of its ~50 binaries
|
||||||
|
|
||||||
unzip.sandbox.method = "bwrap";
|
unzip.sandbox.method = "bwrap";
|
||||||
unzip.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
unzip.sandbox.autodetectCliPaths = "existingOrParent";
|
unzip.sandbox.autodetectCliPaths = "existingOrParent";
|
||||||
unzip.sandbox.whitelistPwd = true;
|
unzip.sandbox.whitelistPwd = true;
|
||||||
|
|
||||||
usbutils.sandbox.method = "bwrap"; # breaks `usbhid-dump`, but `lsusb`, `usb-devices` work
|
usbutils.sandbox.method = "bwrap"; # breaks `usbhid-dump`, but `lsusb`, `usb-devices` work
|
||||||
usbutils.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
usbutils.sandbox.extraPaths = [
|
usbutils.sandbox.extraPaths = [
|
||||||
"/sys/devices"
|
"/sys/devices"
|
||||||
"/sys/bus/usb"
|
"/sys/bus/usb"
|
||||||
];
|
];
|
||||||
|
|
||||||
visidata.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
visidata.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
visidata.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
visidata.sandbox.autodetectCliPaths = true;
|
visidata.sandbox.autodetectCliPaths = true;
|
||||||
|
|
||||||
# `vulkaninfo`, `vkcube`
|
# `vulkaninfo`, `vkcube`
|
||||||
vulkan-tools.sandbox.method = "landlock";
|
vulkan-tools.sandbox.method = "landlock";
|
||||||
vulkan-tools.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
|
|
||||||
vvvvvv.sandbox.method = "bwrap";
|
vvvvvv.sandbox.method = "bwrap";
|
||||||
vvvvvv.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
vvvvvv.sandbox.whitelistAudio = true;
|
vvvvvv.sandbox.whitelistAudio = true;
|
||||||
vvvvvv.sandbox.whitelistDri = true; #< playable without, but burns noticably more CPU
|
vvvvvv.sandbox.whitelistDri = true; #< playable without, but burns noticably more CPU
|
||||||
vvvvvv.sandbox.whitelistWayland = true;
|
vvvvvv.sandbox.whitelistWayland = true;
|
||||||
vvvvvv.persist.byStore.plaintext = [ ".local/share/VVVVVV" ];
|
vvvvvv.persist.byStore.plaintext = [ ".local/share/VVVVVV" ];
|
||||||
|
|
||||||
w3m.sandbox.method = "bwrap";
|
w3m.sandbox.method = "bwrap";
|
||||||
w3m.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
w3m.sandbox.net = "all";
|
w3m.sandbox.net = "all";
|
||||||
w3m.sandbox.extraHomePaths = [
|
w3m.sandbox.extraHomePaths = [
|
||||||
# little-used feature, but you can save web pages :)
|
# little-used feature, but you can save web pages :)
|
||||||
@@ -939,11 +894,9 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
wdisplays.sandbox.method = "bwrap";
|
wdisplays.sandbox.method = "bwrap";
|
||||||
wdisplays.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
wdisplays.sandbox.whitelistWayland = true;
|
wdisplays.sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
wget.sandbox.method = "bwrap";
|
wget.sandbox.method = "bwrap";
|
||||||
wget.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
wget.sandbox.net = "all";
|
wget.sandbox.net = "all";
|
||||||
wget.sandbox.whitelistPwd = true; # saves to pwd by default
|
wget.sandbox.whitelistPwd = true; # saves to pwd by default
|
||||||
|
|
||||||
@@ -951,40 +904,35 @@ in
|
|||||||
|
|
||||||
# `wg`, `wg-quick`
|
# `wg`, `wg-quick`
|
||||||
wireguard-tools.sandbox.method = "landlock";
|
wireguard-tools.sandbox.method = "landlock";
|
||||||
wireguard-tools.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
wireguard-tools.sandbox.capabilities = [ "net_admin" ];
|
wireguard-tools.sandbox.capabilities = [ "net_admin" ];
|
||||||
|
|
||||||
# provides `iwconfig`, `iwlist`, `iwpriv`, ...
|
# provides `iwconfig`, `iwlist`, `iwpriv`, ...
|
||||||
wirelesstools.sandbox.method = "landlock";
|
wirelesstools.sandbox.method = "landlock";
|
||||||
wirelesstools.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
wirelesstools.sandbox.capabilities = [ "net_admin" ];
|
wirelesstools.sandbox.capabilities = [ "net_admin" ];
|
||||||
|
|
||||||
wl-clipboard.sandbox.method = "bwrap";
|
wl-clipboard.sandbox.method = "bwrap";
|
||||||
wl-clipboard.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
wl-clipboard.sandbox.whitelistWayland = true;
|
wl-clipboard.sandbox.whitelistWayland = true;
|
||||||
|
|
||||||
|
wtype = {};
|
||||||
|
|
||||||
xwayland.sandbox.method = "bwrap";
|
xwayland.sandbox.method = "bwrap";
|
||||||
xwayland.sandbox.wrapperType = "inplace"; #< consumers use it as a library (e.g. wlroots)
|
xwayland.sandbox.wrapperType = "inplace"; #< consumers use it as a library (e.g. wlroots)
|
||||||
xwayland.sandbox.whitelistWayland = true; #< just assuming this is needed
|
xwayland.sandbox.whitelistWayland = true; #< just assuming this is needed
|
||||||
xwayland.sandbox.net = "clearnet"; #< just assuming this is needed (X11 traffic)
|
xwayland.sandbox.whitelistX = true;
|
||||||
xwayland.sandbox.whitelistDri = true; #< would assume this gives better gfx perf
|
xwayland.sandbox.whitelistDri = true; #< would assume this gives better gfx perf
|
||||||
|
|
||||||
xdg-terminal-exec.sandbox.enable = false; # xdg-terminal-exec is a launcher for $TERM
|
|
||||||
xterm.sandbox.enable = false; # need to be able to do everything
|
xterm.sandbox.enable = false; # need to be able to do everything
|
||||||
|
|
||||||
yarn.persist.byStore.plaintext = [ ".cache/yarn" ];
|
yarn.persist.byStore.plaintext = [ ".cache/yarn" ];
|
||||||
|
|
||||||
yt-dlp.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
yt-dlp.sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
yt-dlp.sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
yt-dlp.sandbox.net = "all";
|
yt-dlp.sandbox.net = "all";
|
||||||
yt-dlp.sandbox.whitelistPwd = true; # saves to pwd by default
|
yt-dlp.sandbox.whitelistPwd = true; # saves to pwd by default
|
||||||
|
|
||||||
|
zfs = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.feedbackd = lib.mkIf config.sane.programs.feedbackd.enabled {
|
programs.feedbackd = lib.mkIf config.sane.programs.feedbackd.enabled {
|
||||||
enable = true;
|
enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.firejail = lib.mkIf config.sane.programs.firejail.enabled {
|
|
||||||
enable = true; #< install the suid binary
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,8 @@
|
|||||||
|
# tips/tricks
|
||||||
|
# - audio recording
|
||||||
|
# - default recording input will be silent, on lappy.
|
||||||
|
# - Audio Setup -> Rescan Audio Devices ...
|
||||||
|
# - Audio Setup -> Recording device -> sysdefault
|
||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
sane.programs.audacity = {
|
sane.programs.audacity = {
|
||||||
@@ -10,7 +15,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
sandbox.autodetectCliPaths = true;
|
sandbox.autodetectCliPaths = true;
|
||||||
@@ -21,6 +25,9 @@
|
|||||||
# audacity needs the entire config dir mounted if running in a sandbox
|
# audacity needs the entire config dir mounted if running in a sandbox
|
||||||
".config/audacity"
|
".config/audacity"
|
||||||
];
|
];
|
||||||
|
sandbox.extraPaths = [
|
||||||
|
"/dev/snd" # for recording audio inputs to work
|
||||||
|
];
|
||||||
|
|
||||||
# disable first-run splash screen
|
# disable first-run splash screen
|
||||||
fs.".config/audacity/audacity.cfg".file.text = ''
|
fs.".config/audacity/audacity.cfg".file.text = ''
|
||||||
|
@@ -88,7 +88,6 @@ in
|
|||||||
{
|
{
|
||||||
sane.programs.bemenu = {
|
sane.programs.bemenu = {
|
||||||
sandbox.method = "bwrap"; # landlock works, but requires *all* of /run/user/$ID to be granted.
|
sandbox.method = "bwrap"; # landlock works, but requires *all* of /run/user/$ID to be granted.
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
sandbox.extraHomePaths = [
|
sandbox.extraHomePaths = [
|
||||||
".cache/fontconfig" #< else it complains, and is *way* slower
|
".cache/fontconfig" #< else it complains, and is *way* slower
|
||||||
|
216
hosts/common/programs/blast-ugjka/blast-to-default
Executable file
216
hosts/common/programs/blast-ugjka/blast-to-default
Executable file
@@ -0,0 +1,216 @@
|
|||||||
|
#!/usr/bin/env nix-shell
|
||||||
|
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p blast-ugjka
|
||||||
|
# vim: set filetype=python :
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# map from known devices -> required flags
|
||||||
|
DEVICE_MAP = {
|
||||||
|
"Theater TV": [],
|
||||||
|
"[LG] webOS TV OLED55C9PUA": [ "-usewav" ],
|
||||||
|
}
|
||||||
|
|
||||||
|
def set_pdeathsig(sig=signal.SIGTERM):
|
||||||
|
"""
|
||||||
|
helper function to ensure once parent process exits, its children processes will automatically die.
|
||||||
|
see: <https://stackoverflow.com/a/43152455>
|
||||||
|
see: <https://www.man7.org/linux/man-pages/man2/prctl.2.html>
|
||||||
|
"""
|
||||||
|
libc = ctypes.CDLL("libc.so.6")
|
||||||
|
return libc.prctl(1, sig)
|
||||||
|
|
||||||
|
MY_PID = None
|
||||||
|
|
||||||
|
def reap_children(sig=None, frame=None):
|
||||||
|
global MY_PID
|
||||||
|
# reset SIGTERM handler to avoid recursing
|
||||||
|
signal.signal(signal.SIGTERM, signal.Handlers.SIG_DFL)
|
||||||
|
logger.info("killing all children (of pid %d)", MY_PID)
|
||||||
|
os.killpg(MY_PID, signal.SIGTERM)
|
||||||
|
|
||||||
|
def reap_on_exit():
|
||||||
|
"""
|
||||||
|
catch when the parent exits, and map that to SIGTERM for this process.
|
||||||
|
when this process receives SIGTERM, also terminate all descendent processes.
|
||||||
|
|
||||||
|
this is done because:
|
||||||
|
1. mpv invokes this, but (potentially) via the sandbox wrapper.
|
||||||
|
2. when mpv exits, it `SIGKILL`s that sandbox wrapper.
|
||||||
|
3. bwrap does not pass SIGKILL or SIGTERM to its child.
|
||||||
|
4. hence, we neither receive that signal NOR can we pass it on simply by killing our immediate children
|
||||||
|
(since any bwrap'd children wouldn't pass that signal on...)
|
||||||
|
really, the proper fix would be on mpv's side:
|
||||||
|
- mpv should create a new process group when it launches a command, and kill that process group on exit.
|
||||||
|
or fix this in the sandbox wrapper:
|
||||||
|
- why *doesn't* bwrap forward the signals?
|
||||||
|
- there's --die-with-parent, but i can't apply that *system wide* and expect reasonably behavior
|
||||||
|
<https://github.com/containers/bubblewrap/issues/529>
|
||||||
|
"""
|
||||||
|
global MY_PID
|
||||||
|
MY_PID = os.getpid()
|
||||||
|
# create a new process group, pgid = gid
|
||||||
|
os.setpgid(MY_PID, MY_PID)
|
||||||
|
|
||||||
|
set_pdeathsig(signal.SIGTERM)
|
||||||
|
signal.signal(signal.SIGTERM, reap_children)
|
||||||
|
|
||||||
|
def get_ranked_ip_addrs():
|
||||||
|
"""
|
||||||
|
return the IP addresses most likely to be LAN addresses
|
||||||
|
based on: <https://stackoverflow.com/a/1267524>
|
||||||
|
"""
|
||||||
|
_name, _aliases, static_addrs = socket.gethostbyname_ex(socket.gethostname())
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.connect(("1", 53))
|
||||||
|
con_addr, _port = s.getsockname()
|
||||||
|
return sorted(set(static_addrs + [ con_addr ]), key=lambda a: (a.startswith("127"), a))
|
||||||
|
|
||||||
|
|
||||||
|
class ParserState(Enum):
|
||||||
|
Break = "break"
|
||||||
|
Receiver = "receiver"
|
||||||
|
Ips = "ip"
|
||||||
|
|
||||||
|
class Status(Enum):
|
||||||
|
Continue = "continue"
|
||||||
|
Error = "error"
|
||||||
|
RedoWithFlags = "redo_with_flags"
|
||||||
|
Launched = "launched"
|
||||||
|
|
||||||
|
class BlastDriver:
|
||||||
|
parsing: ParserState | None = None
|
||||||
|
last_write: str | None = None
|
||||||
|
def __init__(self, blast_flags: list[str] = []):
|
||||||
|
self.ranked_ips = get_ranked_ip_addrs()
|
||||||
|
self.blast = subprocess.Popen(
|
||||||
|
["blast", "-source", "blast.monitor"] + blast_flags,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
# this pdeathsig isn't necessary; seems it might result in leaked pulse outputs
|
||||||
|
# preexec_fn=set_pdeathsig
|
||||||
|
)
|
||||||
|
self.blast_flags = list(blast_flags)
|
||||||
|
self.receiver_names = []
|
||||||
|
self.ips = []
|
||||||
|
|
||||||
|
def writeline(self, line: str) -> None:
|
||||||
|
logger.debug("[send] %s", line)
|
||||||
|
self.blast.stdin.write(f"{line}\n".encode())
|
||||||
|
self.blast.stdin.flush()
|
||||||
|
self.last_write = line
|
||||||
|
|
||||||
|
def readline(self) -> str:
|
||||||
|
line = self.blast.stdout.readline().decode('utf-8').strip()
|
||||||
|
line = line.replace('\x1b[1A\x1b[K', '') #< escape codes
|
||||||
|
logger.debug("[recv] %r", line)
|
||||||
|
return line
|
||||||
|
|
||||||
|
def set_state(self, state: ParserState):
|
||||||
|
logger.debug("[pars] %s", state)
|
||||||
|
self.parsing = state
|
||||||
|
|
||||||
|
def feedline(self, line: str) -> (Status, str|None):
|
||||||
|
"""
|
||||||
|
apply a line from blast's stdout to modify parser state.
|
||||||
|
returns a status code (e.g. Status.Continue), and optionally a reply to send back to blast.
|
||||||
|
"""
|
||||||
|
if line == "Loading...":
|
||||||
|
return Status.Continue, None
|
||||||
|
elif line == "----------":
|
||||||
|
self.set_state(ParserState.Break)
|
||||||
|
return Status.Continue, None
|
||||||
|
elif line == "DLNA receivers":
|
||||||
|
self.set_state(ParserState.Receiver)
|
||||||
|
return Status.Continue, None
|
||||||
|
elif line == "Your LAN ip addresses":
|
||||||
|
self.set_state(ParserState.Ips)
|
||||||
|
return Status.Continue, None
|
||||||
|
elif line == "Select the DLNA device:":
|
||||||
|
assert len(self.receiver_names) == 1, self.receiver_names
|
||||||
|
name = self.receiver_names[0]
|
||||||
|
if name in DEVICE_MAP and DEVICE_MAP[name] != self.blast_flags:
|
||||||
|
return Status.RedoWithFlags, None
|
||||||
|
return Status.Continue, "0"
|
||||||
|
elif line == "Select the lan IP address for the stream:":
|
||||||
|
for r in self.ranked_ips:
|
||||||
|
if r in self.ips:
|
||||||
|
return Status.Launched, str(self.ips.index(r))
|
||||||
|
# fallback: just guess the best IP
|
||||||
|
return Status.Launched, "0"
|
||||||
|
elif self.parsing == ParserState.Receiver:
|
||||||
|
id_, name = line.split(": ")
|
||||||
|
assert id_ == str(len(self.receiver_names)), (id_, self.receiver_names)
|
||||||
|
self.receiver_names.append(name)
|
||||||
|
return Status.Continue, None
|
||||||
|
elif self.parsing == ParserState.Ips:
|
||||||
|
id_, ip = line.split(": ")
|
||||||
|
assert id_ == str(len(self.ips)), (id_, self.ips)
|
||||||
|
self.ips.append(ip)
|
||||||
|
return Status.Continue, None
|
||||||
|
elif line == f"[{self.last_write}]":
|
||||||
|
# it's echoing to us what we wrote
|
||||||
|
return Status.Continue, None
|
||||||
|
# elif line == "":
|
||||||
|
# return Status.Continue, None
|
||||||
|
else:
|
||||||
|
logger.info("unrecognized output (state=%s): %r", self.parsing, line)
|
||||||
|
return Status.Error, None
|
||||||
|
|
||||||
|
def step(self) -> Status:
|
||||||
|
"""
|
||||||
|
advance the interaction between us and blast.
|
||||||
|
reads a line from blast, modifies internal state, maybe sends a reply.
|
||||||
|
could block indefinitely.
|
||||||
|
"""
|
||||||
|
line = self.readline()
|
||||||
|
status, reply = self.feedline(line)
|
||||||
|
if reply is not None:
|
||||||
|
self.writeline(reply)
|
||||||
|
return status
|
||||||
|
|
||||||
|
def try_blast(*args, **kwargs) -> BlastDriver | None:
|
||||||
|
blast = BlastDriver(*args, **kwargs)
|
||||||
|
status = Status.Continue
|
||||||
|
while status == Status.Continue:
|
||||||
|
status = blast.step()
|
||||||
|
|
||||||
|
if status == Status.RedoWithFlags:
|
||||||
|
dev = blast.receiver_names[0]
|
||||||
|
blast_flags = DEVICE_MAP[dev]
|
||||||
|
logger.info("re-exec blast for %s with flags: %r", dev, blast_flags)
|
||||||
|
blast.blast.terminate()
|
||||||
|
return try_blast(blast_flags=blast_flags)
|
||||||
|
elif status == Status.Error:
|
||||||
|
logger.info("blast error => terminating")
|
||||||
|
blast.blast.terminate()
|
||||||
|
else:
|
||||||
|
# successfully launched
|
||||||
|
return blast
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
logging.basicConfig()
|
||||||
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
reap_on_exit()
|
||||||
|
|
||||||
|
blast = try_blast()
|
||||||
|
|
||||||
|
if blast is not None:
|
||||||
|
logger.info("waiting until blast exits")
|
||||||
|
blast.blast.wait()
|
||||||
|
|
||||||
|
reap_children()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
51
hosts/common/programs/blast-ugjka/default.nix
Normal file
51
hosts/common/programs/blast-ugjka/default.nix
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# blast: tunnel audio from a pulseaudio sink to a UPnP/DLNA device (like a TV).
|
||||||
|
# - expect 7s of latency
|
||||||
|
# - can cast the default sink, or create a new one "blast.monitor"
|
||||||
|
# and either assign that to default or assign apps to it.
|
||||||
|
# compatibility:
|
||||||
|
# - there is no single invocation which will be compatible with all known devices.
|
||||||
|
# - sony tv:
|
||||||
|
# - `blast` (default): WORKS
|
||||||
|
# - `-usewav`: FAILS
|
||||||
|
# - LG TV:
|
||||||
|
# - `-usewav`: WORKS!
|
||||||
|
# - `-useaac`: FAILS
|
||||||
|
# - `-useflac`: FAILS
|
||||||
|
# - `-uselpcm`: FAILS
|
||||||
|
# - `-uselpcmle`: FAILS
|
||||||
|
# - `-format aac`: FAILS
|
||||||
|
# - `-bitrate 128`: FAILS
|
||||||
|
# - `-nochunked`: FAILS
|
||||||
|
# - `-format "ogg" -mime 'audio/x-opus+ogg'`: FAILS
|
||||||
|
# - `-mime audio/ac3 -format ac3`: FAILS
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.sane.programs.blast-ugjka;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sane.programs.blast-ugjka = {
|
||||||
|
sandbox.method = "bwrap";
|
||||||
|
sandbox.whitelistAudio = true;
|
||||||
|
sandbox.net = "clearnet";
|
||||||
|
};
|
||||||
|
|
||||||
|
sane.programs.blast-to-default = {
|
||||||
|
# helper to deal with blast's interactive CLI
|
||||||
|
packageUnwrapped = pkgs.static-nix-shell.mkPython3Bin {
|
||||||
|
pname = "blast-to-default";
|
||||||
|
pkgs = [ "blast-ugjka" ];
|
||||||
|
srcRoot = ./.;
|
||||||
|
};
|
||||||
|
sandbox.method = "bwrap";
|
||||||
|
sandbox.whitelistAudio = true;
|
||||||
|
sandbox.net = "clearnet";
|
||||||
|
sandbox.extraConfig = [
|
||||||
|
# else it fails to reap its children (or, maybe, it fails to hook its parent's death signal?)
|
||||||
|
# might be possible to remove this, but kinda hard to see a clean way.
|
||||||
|
"--sane-sandbox-keep-namespace" "pid"
|
||||||
|
];
|
||||||
|
suggestedPrograms = [ "blast-ugjka" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = lib.mkIf cfg.enabled [ 9000 ];
|
||||||
|
}
|
@@ -111,20 +111,16 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sandbox.method = "bwrap";
|
||||||
|
sandbox.extraRuntimePaths = [
|
||||||
|
"/" #< just needs "bonsai", but needs to create it first...
|
||||||
|
];
|
||||||
|
|
||||||
services.bonsaid = {
|
services.bonsaid = {
|
||||||
description = "bonsai: programmable input dispatcher";
|
description = "bonsai: programmable input dispatcher";
|
||||||
after = [ "graphical-session.target" ];
|
partOf = [ "graphical-session" ];
|
||||||
wantedBy = [ "graphical-session.target" ];
|
command = "bonsaid -t ${cfg.config.configFile}";
|
||||||
|
cleanupCommand = "rm -f $XDG_RUNTIME_DIR/bonsai";
|
||||||
script = ''
|
|
||||||
${pkgs.coreutils}/bin/rm -f $XDG_RUNTIME_DIR/bonsai
|
|
||||||
exec ${cfg.package}/bin/bonsaid -t ${cfg.config.configFile}
|
|
||||||
'';
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "simple";
|
|
||||||
Restart = "always";
|
|
||||||
RestartSec = "5s";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -21,12 +21,12 @@
|
|||||||
# note that invoking bwrap with capabilities in the 'init' namespace does NOT grant the sandboxed process
|
# note that invoking bwrap with capabilities in the 'init' namespace does NOT grant the sandboxed process
|
||||||
# capabilities in the 'init' namespace. it's a limitation of namespaces that namespaced processes can
|
# capabilities in the 'init' namespace. it's a limitation of namespaces that namespaced processes can
|
||||||
# never receive capabilities in their parent namespace.
|
# never receive capabilities in their parent namespace.
|
||||||
substituteInPlace bubblewrap.c --replace \
|
substituteInPlace bubblewrap.c --replace-fail \
|
||||||
'die ("Unexpected capabilities but not setuid, old file caps config?");' \
|
'die ("Unexpected capabilities but not setuid, old file caps config?");' \
|
||||||
'// die ("Unexpected capabilities but not setuid, old file caps config?");'
|
'// die ("Unexpected capabilities but not setuid, old file caps config?");'
|
||||||
|
|
||||||
# enable debug printing
|
# enable debug printing
|
||||||
# substituteInPlace utils.h --replace \
|
# substituteInPlace utils.h --replace-fail \
|
||||||
# '#define __debug__(x)' \
|
# '#define __debug__(x)' \
|
||||||
# '#define __debug__(x) printf x'
|
# '#define __debug__(x) printf x'
|
||||||
'';
|
'';
|
||||||
|
@@ -44,15 +44,9 @@ in
|
|||||||
services.gnome-calls = {
|
services.gnome-calls = {
|
||||||
# TODO: prevent gnome-calls from daemonizing when started manually
|
# TODO: prevent gnome-calls from daemonizing when started manually
|
||||||
description = "gnome-calls daemon to monitor incoming SIP calls";
|
description = "gnome-calls daemon to monitor incoming SIP calls";
|
||||||
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
|
partOf = lib.mkIf cfg.config.autostart [ "graphical-session" ];
|
||||||
serviceConfig = {
|
# add --verbose for more debugging
|
||||||
# add --verbose for more debugging
|
command = "env G_MESSAGES_DEBUG=all gnome-calls --daemon";
|
||||||
ExecStart = "${cfg.package}/bin/gnome-calls --daemon";
|
|
||||||
Type = "simple";
|
|
||||||
Restart = "always";
|
|
||||||
RestartSec = "10s";
|
|
||||||
};
|
|
||||||
environment.G_MESSAGES_DEBUG = "all";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
programs.calls = lib.mkIf cfg.enabled {
|
programs.calls = lib.mkIf cfg.enabled {
|
||||||
|
17
hosts/common/programs/celeste64.nix
Normal file
17
hosts/common/programs/celeste64.nix
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
sane.programs.celeste64 = {
|
||||||
|
sandbox.method = "bwrap";
|
||||||
|
sandbox.whitelistAudio = true;
|
||||||
|
sandbox.whitelistDri = true;
|
||||||
|
sandbox.whitelistWayland = true;
|
||||||
|
sandbox.extraPaths = [
|
||||||
|
"/dev/input" #< for controllers
|
||||||
|
];
|
||||||
|
|
||||||
|
persist.byStore.plaintext = [
|
||||||
|
# save data, controls map
|
||||||
|
".local/share/Celeste64"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
{ config, pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
sane.programs.conky = {
|
sane.programs.conky = {
|
||||||
# TODO: non-sandboxed `conky` still ships via `sxmo-utils`, but unused
|
# TODO: non-sandboxed `conky` still ships via `sxmo-utils`, but unused
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
fs.".config/conky/conky.conf".symlink.target =
|
fs.".config/conky/conky.conf".symlink.target =
|
||||||
let
|
let
|
||||||
|
# TODO: make this just another `suggestedPrograms`!
|
||||||
battery_estimate = pkgs.static-nix-shell.mkBash {
|
battery_estimate = pkgs.static-nix-shell.mkBash {
|
||||||
pname = "battery_estimate";
|
pname = "battery_estimate";
|
||||||
srcRoot = ./.;
|
srcRoot = ./.;
|
||||||
@@ -26,14 +27,8 @@
|
|||||||
|
|
||||||
services.conky = {
|
services.conky = {
|
||||||
description = "conky dynamic desktop background";
|
description = "conky dynamic desktop background";
|
||||||
after = [ "graphical-session.target" ];
|
partOf = [ "graphical-session" ];
|
||||||
# partOf = [ "graphical-session.target" ]; # propagate stop/restart signal from graphical-session to this unit
|
command = "conky";
|
||||||
wantedBy = [ "graphical-session.target" ];
|
|
||||||
|
|
||||||
serviceConfig.ExecStart = "${config.sane.programs.conky.package}/bin/conky";
|
|
||||||
serviceConfig.Type = "simple";
|
|
||||||
serviceConfig.Restart = "on-failure";
|
|
||||||
serviceConfig.RestartSec = "10s";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,19 @@
|
|||||||
{ ... }:
|
{ pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.programs.cozy = {
|
sane.programs.cozy = {
|
||||||
|
packageUnwrapped = pkgs.cozy.overrideAttrs (upstream: {
|
||||||
|
postPatch = (upstream.postPatch or "") + ''
|
||||||
|
# disable all reporting.
|
||||||
|
# this can be done via the settings, but that's troublesome and easy to forget.
|
||||||
|
# specifically, i don't want moby to be making these network requests several times per hour
|
||||||
|
# while it might be roaming or trying to put the RF to sleep.
|
||||||
|
substituteInPlace cozy/application_settings.py \
|
||||||
|
--replace-fail 'self._settings.get_int("report-level")' '0'
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
|
||||||
sandbox.method = "bwrap"; # landlock gives: _multiprocessing.SemLock: Permission Denied
|
sandbox.method = "bwrap"; # landlock gives: _multiprocessing.SemLock: Permission Denied
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
sandbox.whitelistDbus = [ "user" ]; # mpris
|
sandbox.whitelistDbus = [ "user" ]; # mpris
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
|
25
hosts/common/programs/curlftpfs.nix
Normal file
25
hosts/common/programs/curlftpfs.nix
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
sane.programs.curlftpfs = {
|
||||||
|
packageUnwrapped = pkgs.curlftpfs.overrideAttrs (upstream: {
|
||||||
|
# my fork includes:
|
||||||
|
# - per-operation timeouts (CURLOPT_TIMEOUT; would use CURLOPT_LOW_SPEED_TIME/CURLOPT_LOW_SPEED_LIMIT but they don't apply)
|
||||||
|
# - exit on timeout (so that one knows to abort the mount, instead of waiting indefinitely)
|
||||||
|
# - support for "meta" keys found in /etc/fstab
|
||||||
|
src = pkgs.fetchFromGitea {
|
||||||
|
domain = "git.uninsane.org";
|
||||||
|
owner = "colin";
|
||||||
|
repo = "curlftpfs";
|
||||||
|
rev = "0890d32e709b5a01153f00d29ed4c00299744f5d";
|
||||||
|
hash = "sha256-M28PzHqEAkezQdtPeL16z56prwl3BfMZqry0dlpXJls=";
|
||||||
|
};
|
||||||
|
# `mount` clears PATH before calling the mount helper (see util-linux/lib/env.c),
|
||||||
|
# so the traditional /etc/fstab approach of fstype=fuse and device = curlftpfs#URI doesn't work.
|
||||||
|
# instead, install a `mount.curlftpfs` mount helper. this is what programs like `gocryptfs` do.
|
||||||
|
postInstall = (upstream.postInstall or "") + ''
|
||||||
|
ln -s curlftpfs $out/bin/mount.fuse.curlftpfs
|
||||||
|
ln -s curlftpfs $out/bin/mount.curlftpfs
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
@@ -9,17 +9,40 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
sane.programs.dconf = {
|
sane.programs.dconf = {
|
||||||
|
configOption = with lib; mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
options = {
|
||||||
|
site = mkOption {
|
||||||
|
type = types.listOf types.package;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
extra packages to link into /etc/dconf
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
packageUnwrapped = pkgs.rmDbusServicesInPlace pkgs.dconf;
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
sandbox.wrapperType = "inplace"; #< dbus/systemd services live in `.out` but point to `.lib` data.
|
||||||
|
sandbox.whitelistDbus = [ "user" ];
|
||||||
persist.byStore.private = [
|
persist.byStore.private = [
|
||||||
".config/dconf"
|
".config/dconf"
|
||||||
];
|
];
|
||||||
};
|
|
||||||
|
|
||||||
programs.dconf = lib.mkIf cfg.enabled {
|
services.dconf = {
|
||||||
# note that `programs.dconf` doesn't allow specifying the dconf package.
|
description = "dconf configuration database/server";
|
||||||
enable = true;
|
partOf = [ "graphical-session" ];
|
||||||
packages = [
|
command = "${lib.getLib cfg.package}/libexec/dconf-service";
|
||||||
|
};
|
||||||
|
|
||||||
|
# supposedly necessary for packages which haven't been wrapped (i.e. wrapGtkApp?),
|
||||||
|
# but in practice seems unnecessary.
|
||||||
|
# env.GIO_EXTRA_MODULES = "${pkgs.dconf.lib}/lib/gio/modules";
|
||||||
|
|
||||||
|
config.site = [
|
||||||
(pkgs.writeTextFile {
|
(pkgs.writeTextFile {
|
||||||
name = "dconf-user-profile";
|
name = "dconf-user-profile";
|
||||||
destination = "/etc/dconf/profile/user";
|
destination = "/etc/dconf/profile/user";
|
||||||
@@ -30,4 +53,18 @@ in
|
|||||||
})
|
})
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# TODO: get dconf to read these from ~/.config/dconf ?
|
||||||
|
environment.etc.dconf = lib.mkIf cfg.enabled {
|
||||||
|
source = pkgs.symlinkJoin {
|
||||||
|
name = "dconf-system-config";
|
||||||
|
paths = map (x: "${x}/etc/dconf") cfg.config.site;
|
||||||
|
nativeBuildInputs = [ (lib.getBin pkgs.dconf) ];
|
||||||
|
postBuild = ''
|
||||||
|
if test -d $out/db; then
|
||||||
|
dconf update $out/db
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
139
hosts/common/programs/deadd-notification-center/deadd.css
Normal file
139
hosts/common/programs/deadd-notification-center/deadd.css
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
|
||||||
|
/* Notification center */
|
||||||
|
|
||||||
|
.blurredBG, #main_window, .blurredBG.low, .blurredBG.normal {
|
||||||
|
background: rgba(255, 255, 255, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.noti-center.time {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Notifications */
|
||||||
|
|
||||||
|
.notification.content {
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appname {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blurredBG.notification {
|
||||||
|
background: rgba(255, 255, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.blurredBG.notification.critical {
|
||||||
|
background: rgba(255, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notificationInCenter.critical {
|
||||||
|
background: rgba(155, 0, 20, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Labels */
|
||||||
|
|
||||||
|
label {
|
||||||
|
color: #322;
|
||||||
|
}
|
||||||
|
|
||||||
|
label.notification {
|
||||||
|
color: #322;
|
||||||
|
}
|
||||||
|
|
||||||
|
label.critical {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.notificationInCenter label.critical {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: transparent;
|
||||||
|
color: #322;
|
||||||
|
border-radius: 3px;
|
||||||
|
border-width: 0px;
|
||||||
|
background-position: 0px 0px;
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
border-radius: 3px;
|
||||||
|
background: rgba(0, 20, 20, 0.2);
|
||||||
|
border-width: 0px;
|
||||||
|
border-top: transparent;
|
||||||
|
border-color: #f00;
|
||||||
|
color: #fee;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Custom Buttons */
|
||||||
|
|
||||||
|
.userbutton {
|
||||||
|
background: rgba(20,0,0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.userbuttonlabel {
|
||||||
|
color: #222;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userbutton:hover {
|
||||||
|
background: rgba(20, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.userbuttonlabel:hover {
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.buttonState1 {
|
||||||
|
background: rgba(20,0,0,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.userbuttonlabel.buttonState1 {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.buttonState1:hover {
|
||||||
|
background: rgba(20,0,0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.userbuttonlabel.buttonState1:hover {
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.buttonState2 {
|
||||||
|
background: rgba(255,255,255,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.userbuttonlabel.buttonState2 {
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.buttonState2:hover {
|
||||||
|
background: rgba(20,0,0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.userbuttonlabel.buttonState2:hover {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Images */
|
||||||
|
|
||||||
|
image.deadd-noti-center.notification.image {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
263
hosts/common/programs/deadd-notification-center/deadd.yml
Normal file
263
hosts/common/programs/deadd-notification-center/deadd.yml
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
### Margins for notification-center/notifications
|
||||||
|
margin-top: 0
|
||||||
|
margin-right: 0
|
||||||
|
|
||||||
|
### Margins for notification-center
|
||||||
|
margin-bottom: 0
|
||||||
|
|
||||||
|
### Width of the notification center/notifications in pixels.
|
||||||
|
width: 360
|
||||||
|
|
||||||
|
### Command to run at startup. This can be used to setup
|
||||||
|
### button states.
|
||||||
|
# startup-command: deadd-notification-center-startup
|
||||||
|
|
||||||
|
### Monitor on which the notification center/notifications will be
|
||||||
|
### printed. If "follow-mouse" is set true, this does nothing.
|
||||||
|
monitor: 0
|
||||||
|
|
||||||
|
### If true, the notification center/notifications will open on the
|
||||||
|
### screen, on which the mouse is. Overrides the "monitor" setting.
|
||||||
|
follow-mouse: false
|
||||||
|
|
||||||
|
notification-center:
|
||||||
|
### Margin at the top/right/bottom of the notification center in
|
||||||
|
### pixels. This can be used to avoid overlap between the notification
|
||||||
|
### center and bars such as polybar or i3blocks.
|
||||||
|
margin-top: 40
|
||||||
|
# margin-right: 0
|
||||||
|
# margin-bottom: 0
|
||||||
|
|
||||||
|
### Width of the notification center in pixels.
|
||||||
|
# width: 500
|
||||||
|
|
||||||
|
### Monitor on which the notification center will be printed. If
|
||||||
|
### "follow-mouse" is set true, this does nothing.
|
||||||
|
# monitor: 0
|
||||||
|
|
||||||
|
### If true, the notification center will open on the screen, on which
|
||||||
|
### the mouse is. Overrides the "monitor" setting.
|
||||||
|
# follow-mouse: false
|
||||||
|
|
||||||
|
### Notification center closes when the mouse leaves it
|
||||||
|
hide-on-mouse-leave: true
|
||||||
|
|
||||||
|
### If newFirst is set to true, newest notifications appear on the top
|
||||||
|
### of the notification center. Else, notifications stack, from top to
|
||||||
|
### bottom.
|
||||||
|
new-first: true
|
||||||
|
|
||||||
|
### If true, the transient field in notifications will be ignored,
|
||||||
|
### thus the notification will be persisted in the notification
|
||||||
|
### center anyways
|
||||||
|
ignore-transient: false
|
||||||
|
|
||||||
|
### Custom buttons in notification center
|
||||||
|
buttons:
|
||||||
|
### Numbers of buttons that can be drawn on a row of the notification
|
||||||
|
### center.
|
||||||
|
# buttons-per-row: 5
|
||||||
|
|
||||||
|
### Height of buttons in the notification center (in pixels).
|
||||||
|
# buttons-height: 60
|
||||||
|
|
||||||
|
### Horizontal and vertical margin between each button in the
|
||||||
|
### notification center (in pixels).
|
||||||
|
# buttons-margin: 2
|
||||||
|
|
||||||
|
### Button actions and labels. For each button you must specify a
|
||||||
|
### label and a command.
|
||||||
|
actions:
|
||||||
|
# - label: VPN
|
||||||
|
# command: "sudo vpnToggle"
|
||||||
|
# - label: Bluetooth
|
||||||
|
# command: bluetoothToggle
|
||||||
|
# - label: Wifi
|
||||||
|
# command: wifiToggle
|
||||||
|
# - label: Screensaver
|
||||||
|
# command: screensaverToggle
|
||||||
|
# - label: Keyboard
|
||||||
|
# command: keyboardToggle
|
||||||
|
|
||||||
|
notification:
|
||||||
|
### If true, markup (<u>, <i>, <b>, <a>) will be displayed properly
|
||||||
|
use-markup: true
|
||||||
|
|
||||||
|
### If true, html entities (& for &, % for %, etc) will be
|
||||||
|
### parsed properly. This is useful for chromium-based apps, which
|
||||||
|
### tend to send these in notifications.
|
||||||
|
parse-html-entities: true
|
||||||
|
|
||||||
|
dbus:
|
||||||
|
|
||||||
|
### If noti-closed messages are enabled, the sending application
|
||||||
|
### will know that a notification was closed/timed out. This can
|
||||||
|
### be an issue for certain applications, that overwrite
|
||||||
|
### notifications on status updates (e.g. Spotify on each
|
||||||
|
### song). When one of these applications thinks, the notification
|
||||||
|
### has been closed/timed out, they will not overwrite existing
|
||||||
|
### notifications but send new ones. This can lead to redundant
|
||||||
|
### notifications in the notification center, as the close-message
|
||||||
|
### is send regardless of the notification being persisted.
|
||||||
|
send-noti-closed: false
|
||||||
|
|
||||||
|
app-icon:
|
||||||
|
|
||||||
|
### If set to true: If no icon is passed by the app_icon parameter
|
||||||
|
### and no application "desktop-entry"-hint is present, deadd will
|
||||||
|
### try to guess the icon from the application name (if present).
|
||||||
|
guess-icon-from-name: true
|
||||||
|
|
||||||
|
### The display size of the application icons in the notification
|
||||||
|
### pop-ups and in the notification center
|
||||||
|
icon-size: 20
|
||||||
|
|
||||||
|
image:
|
||||||
|
|
||||||
|
### The maximal display size of images that are part of
|
||||||
|
### notifications for notification pop-ups and in the notification
|
||||||
|
### center
|
||||||
|
size: 100
|
||||||
|
|
||||||
|
### The margin around the top, bottom, left, and right of
|
||||||
|
### notification images.
|
||||||
|
margin-top: 15
|
||||||
|
margin-bottom: 15
|
||||||
|
margin-left: 15
|
||||||
|
margin-right: 0
|
||||||
|
|
||||||
|
### Apply modifications to certain notifications:
|
||||||
|
### Each modification rule needs a "match" and either a "modify" or
|
||||||
|
### a "script" entry.
|
||||||
|
modifications:
|
||||||
|
### Match:
|
||||||
|
### Matches the notifications against these rules. If all of the
|
||||||
|
### values (of one modification rule) match, the "modify"/"script"
|
||||||
|
### part is applied.
|
||||||
|
# - match:
|
||||||
|
### Possible match criteria:
|
||||||
|
# title: "Notification title"
|
||||||
|
# body: "Notification body"
|
||||||
|
# time: "12:44"
|
||||||
|
# app-name: "App name"
|
||||||
|
# urgency: "low" # "low", "normal" or "critical"
|
||||||
|
|
||||||
|
# modify:
|
||||||
|
### Possible modifications
|
||||||
|
# title: "abc"
|
||||||
|
# body: "abc"
|
||||||
|
# app-name: "abc"
|
||||||
|
# app-icon: "file:///abc.png"
|
||||||
|
### The timeout has three special values:
|
||||||
|
### timeout: 0 -> don't time out at all
|
||||||
|
### timeout: -1 -> use default timeout
|
||||||
|
### timeout: 1 -> don't show as pop-up
|
||||||
|
### timeout: >1 -> milliseconds until timeout
|
||||||
|
# timeout: 1
|
||||||
|
# margin-right: 10
|
||||||
|
# margin-top: 10
|
||||||
|
# image: "file:///abc.png"
|
||||||
|
# image-size: 10
|
||||||
|
# transient: true
|
||||||
|
# send-noti-closed: false
|
||||||
|
### Remove action buttons from notifications
|
||||||
|
# remove-actions: true
|
||||||
|
### Set the action-icons hint to true, action labels will then
|
||||||
|
### be intergreted as GTK icon names
|
||||||
|
# action-icons: true
|
||||||
|
### List of actions, where the even elements (0, 2, ...) are the
|
||||||
|
### action name and the odd elements are the label
|
||||||
|
# actions:
|
||||||
|
# - previous
|
||||||
|
# - media-skip-backward
|
||||||
|
# - play
|
||||||
|
# - media-playback-start
|
||||||
|
# - next
|
||||||
|
# - media-skip-forward
|
||||||
|
### Action commands, where the keys (e.g. "play") is the action
|
||||||
|
### name and the value is a program call that should be executed
|
||||||
|
### on action. Prevents sending of the action to the application.
|
||||||
|
# action-commands:
|
||||||
|
# play: playerctl play-pause
|
||||||
|
# previous: playerctl previous
|
||||||
|
# next: playerctl next
|
||||||
|
|
||||||
|
### Add a class-name to the notification container, that can be
|
||||||
|
### used for specific styling of notifications using the
|
||||||
|
### deadd.css file
|
||||||
|
# class-name: "abc"
|
||||||
|
|
||||||
|
# - match:
|
||||||
|
# app-name: "Chromium"
|
||||||
|
|
||||||
|
### Instead of modifying a notification directly, a script can be
|
||||||
|
### run, which will receive the notification as JSON on STDIN. It
|
||||||
|
### is expected to return JSON/YAML configuration that defines the
|
||||||
|
### modifications that should be applied. Minimum complete return
|
||||||
|
### value must be '{"modify": {}, "match": {}}'. Always leave the "match"
|
||||||
|
### object empty (technical reasons, i.e. I am lazy).
|
||||||
|
# script: "linux-notification-center-parse-chromium"
|
||||||
|
- match:
|
||||||
|
app-name: "Spotify"
|
||||||
|
modify:
|
||||||
|
image-size: 80
|
||||||
|
timeout: 1
|
||||||
|
send-noti-closed: true
|
||||||
|
class-name: "Spotify"
|
||||||
|
action-icons: true
|
||||||
|
actions:
|
||||||
|
- previous
|
||||||
|
- media-skip-backward
|
||||||
|
- play
|
||||||
|
- media-playback-start
|
||||||
|
- next
|
||||||
|
- media-skip-forward
|
||||||
|
action-commands:
|
||||||
|
play: playerctl play-pause
|
||||||
|
previous: playerctl previous
|
||||||
|
next: playerctl next
|
||||||
|
|
||||||
|
# - match:
|
||||||
|
# title: Bildschirmhelligkeit
|
||||||
|
# modify:
|
||||||
|
# image-size: 60
|
||||||
|
popup:
|
||||||
|
|
||||||
|
### Default timeout used for notifications in milli-seconds. This can
|
||||||
|
### be overwritten with the "-t" option (or "--expire-time") of the
|
||||||
|
### notify-send command.
|
||||||
|
default-timeout: 10000
|
||||||
|
|
||||||
|
### Margin above/right/between notifications (in pixels). This can
|
||||||
|
### be used to avoid overlap between notifications and a bar such as
|
||||||
|
### polybar or i3blocks.
|
||||||
|
margin-top: 50
|
||||||
|
margin-right: 50
|
||||||
|
margin-between: 20
|
||||||
|
|
||||||
|
### Defines after how many lines of text the body will be truncated.
|
||||||
|
### Use 0 if you want to disable truncation.
|
||||||
|
max-lines-in-body: 3
|
||||||
|
|
||||||
|
### Determines whether the GTK widget that displays the notification body
|
||||||
|
### in the notification popup will be hidden when empty. This is especially
|
||||||
|
### useful for transient notifications that display a progress bar.
|
||||||
|
# hide-body-if-empty: false
|
||||||
|
|
||||||
|
### Monitor on which the notifications will be
|
||||||
|
### printed. If "follow-mouse" is set true, this does nothing.
|
||||||
|
# monitor: 0
|
||||||
|
|
||||||
|
### If true, the notifications will open on the
|
||||||
|
### screen, on which the mouse is. Overrides the "monitor" setting.
|
||||||
|
# follow-mouse: false
|
||||||
|
|
||||||
|
click-behavior:
|
||||||
|
|
||||||
|
### The mouse button for dismissing a popup. Must be either "mouse1",
|
||||||
|
### "mouse2", "mouse3", "mouse4", or "mouse5"
|
||||||
|
dismiss: mouse1
|
||||||
|
|
||||||
|
### The mouse button for opening a popup with the default action.
|
||||||
|
### Must be either "mouse1", "mouse2", "mouse3", "mouse4", or "mouse5"
|
||||||
|
default-action: mouse3
|
17
hosts/common/programs/deadd-notification-center/default.nix
Normal file
17
hosts/common/programs/deadd-notification-center/default.nix
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# docs are via README only:
|
||||||
|
# - <https://github.com/phuhl/linux_notification_center>
|
||||||
|
# reload config:
|
||||||
|
# - `notify-send a --hint=boolean:deadd-notification-center:true --hint=string:type:reloadStyle`
|
||||||
|
# toggle visibility:
|
||||||
|
# - `kill -s USR1 $(pidof deadd-notification-center)`
|
||||||
|
# clear notifications:
|
||||||
|
# - `notify-send a --hint=boolean:deadd-notification-center:true --hint=string:type:clearInCenter`
|
||||||
|
# set state of user button 0 to "highlighted" (true)
|
||||||
|
# - `notify-send a --hint=boolean:deadd-notification-center:true --hint=int:id:0 --hint=boolean:state:true --hint=type:string:buttons`
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
sane.programs.deadd-notification-center = {
|
||||||
|
fs.".config/deadd/deadd.css".symlink.target = ./deadd.css;
|
||||||
|
fs.".config/deadd/deadd.yml".symlink.target = ./deadd.yml;
|
||||||
|
};
|
||||||
|
}
|
@@ -5,27 +5,36 @@
|
|||||||
./abaddon.nix
|
./abaddon.nix
|
||||||
./aerc.nix
|
./aerc.nix
|
||||||
./alacritty.nix
|
./alacritty.nix
|
||||||
|
./alsa-ucm-conf
|
||||||
./animatch.nix
|
./animatch.nix
|
||||||
./assorted.nix
|
./assorted.nix
|
||||||
./audacity.nix
|
./audacity.nix
|
||||||
./bemenu.nix
|
./bemenu.nix
|
||||||
|
./blast-ugjka
|
||||||
./bonsai.nix
|
./bonsai.nix
|
||||||
./brave.nix
|
./brave.nix
|
||||||
./bubblewrap.nix
|
./bubblewrap.nix
|
||||||
./calls.nix
|
./calls.nix
|
||||||
./cantata.nix
|
./cantata.nix
|
||||||
./catt.nix
|
./catt.nix
|
||||||
|
./celeste64.nix
|
||||||
./chatty.nix
|
./chatty.nix
|
||||||
./conky
|
./conky
|
||||||
./cozy.nix
|
./cozy.nix
|
||||||
|
./curlftpfs.nix
|
||||||
./dconf.nix
|
./dconf.nix
|
||||||
|
./deadd-notification-center
|
||||||
./dialect.nix
|
./dialect.nix
|
||||||
./dino.nix
|
./dino.nix
|
||||||
|
./dissent.nix
|
||||||
./element-desktop.nix
|
./element-desktop.nix
|
||||||
|
./engrampa.nix
|
||||||
./epiphany.nix
|
./epiphany.nix
|
||||||
./evince.nix
|
./evince.nix
|
||||||
|
./fcitx5.nix
|
||||||
./feedbackd.nix
|
./feedbackd.nix
|
||||||
./firefox.nix
|
./firefox.nix
|
||||||
|
./firejail.nix
|
||||||
./flare-signal.nix
|
./flare-signal.nix
|
||||||
./fontconfig.nix
|
./fontconfig.nix
|
||||||
./fractal.nix
|
./fractal.nix
|
||||||
@@ -44,14 +53,15 @@
|
|||||||
./gpodder.nix
|
./gpodder.nix
|
||||||
./grimshot.nix
|
./grimshot.nix
|
||||||
./gthumb.nix
|
./gthumb.nix
|
||||||
./gtkcord4.nix
|
|
||||||
./handbrake.nix
|
./handbrake.nix
|
||||||
./helix.nix
|
./helix.nix
|
||||||
|
./htop
|
||||||
./imagemagick.nix
|
./imagemagick.nix
|
||||||
./jellyfin-media-player.nix
|
./jellyfin-media-player.nix
|
||||||
./kdenlive.nix
|
./kdenlive.nix
|
||||||
./komikku.nix
|
./komikku.nix
|
||||||
./koreader
|
./koreader
|
||||||
|
./less.nix
|
||||||
./libreoffice.nix
|
./libreoffice.nix
|
||||||
./lemoa.nix
|
./lemoa.nix
|
||||||
./loupe.nix
|
./loupe.nix
|
||||||
@@ -60,7 +70,7 @@
|
|||||||
./mepo.nix
|
./mepo.nix
|
||||||
./mimeo
|
./mimeo
|
||||||
./mopidy.nix
|
./mopidy.nix
|
||||||
./mpv.nix
|
./mpv
|
||||||
./msmtp.nix
|
./msmtp.nix
|
||||||
./nautilus.nix
|
./nautilus.nix
|
||||||
./neovim.nix
|
./neovim.nix
|
||||||
@@ -80,7 +90,11 @@
|
|||||||
./rhythmbox.nix
|
./rhythmbox.nix
|
||||||
./ripgrep.nix
|
./ripgrep.nix
|
||||||
./rofi
|
./rofi
|
||||||
|
./s6-rc.nix
|
||||||
|
./sane-input-handler
|
||||||
|
./sane-screenshot.nix
|
||||||
./sane-scripts.nix
|
./sane-scripts.nix
|
||||||
|
./schlock.nix
|
||||||
./sfeed.nix
|
./sfeed.nix
|
||||||
./signal-desktop.nix
|
./signal-desktop.nix
|
||||||
./splatmoji.nix
|
./splatmoji.nix
|
||||||
@@ -93,8 +107,10 @@
|
|||||||
./supertuxkart.nix
|
./supertuxkart.nix
|
||||||
./sway
|
./sway
|
||||||
./sway-autoscaler
|
./sway-autoscaler
|
||||||
|
./swayidle.nix
|
||||||
./swaylock.nix
|
./swaylock.nix
|
||||||
./swaynotificationcenter.nix
|
./swaynotificationcenter
|
||||||
|
./sysvol.nix
|
||||||
./tangram.nix
|
./tangram.nix
|
||||||
./tor-browser.nix
|
./tor-browser.nix
|
||||||
./tuba.nix
|
./tuba.nix
|
||||||
@@ -106,20 +122,19 @@
|
|||||||
./wine.nix
|
./wine.nix
|
||||||
./wireplumber.nix
|
./wireplumber.nix
|
||||||
./wireshark.nix
|
./wireshark.nix
|
||||||
./wob
|
./wvkbd.nix
|
||||||
./xarchiver.nix
|
./xarchiver.nix
|
||||||
./xdg-desktop-portal.nix
|
./xdg-desktop-portal.nix
|
||||||
./xdg-desktop-portal-gtk.nix
|
./xdg-desktop-portal-gtk.nix
|
||||||
./xdg-desktop-portal-wlr.nix
|
./xdg-desktop-portal-wlr.nix
|
||||||
|
./xdg-terminal-exec.nix
|
||||||
./xdg-utils.nix
|
./xdg-utils.nix
|
||||||
|
./zathura.nix
|
||||||
./zeal.nix
|
./zeal.nix
|
||||||
./zecwallet-lite.nix
|
./zecwallet-lite.nix
|
||||||
./zsh
|
./zsh
|
||||||
];
|
];
|
||||||
|
|
||||||
config = {
|
# XXX: this might not be necessary. try removing this and cacert.unbundled (servo)?
|
||||||
# XXX: this might not be necessary. try removing this and cacert.unbundled (servo)?
|
environment.etc."ssl/certs".source = "${pkgs.cacert.unbundled}/etc/ssl/certs/*";
|
||||||
environment.etc."ssl/certs".source = "${pkgs.cacert.unbundled}/etc/ssl/certs/*";
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,9 @@
|
|||||||
sandbox.wrapperType = "inplace"; # share/search_providers/ calls back into the binary, weird wrap semantics
|
sandbox.wrapperType = "inplace"; # share/search_providers/ calls back into the binary, weird wrap semantics
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
|
sandbox.extraHomePaths = [
|
||||||
|
".config/dconf" # won't start without it
|
||||||
|
];
|
||||||
suggestedPrograms = [ "dconf" ]; #< to persist settings
|
suggestedPrograms = [ "dconf" ]; #< to persist settings
|
||||||
|
|
||||||
packageUnwrapped = pkgs.dialect.overrideAttrs (upstream: {
|
packageUnwrapped = pkgs.dialect.overrideAttrs (upstream: {
|
||||||
|
@@ -14,6 +14,11 @@
|
|||||||
# but at present it has no "start in tray" type of option: it must render a window.
|
# but at present it has no "start in tray" type of option: it must render a window.
|
||||||
#
|
#
|
||||||
# outstanding bugs:
|
# outstanding bugs:
|
||||||
|
# - NAT holepunching burns CPU/NET when multiple interfaces are up
|
||||||
|
# - fix by just `ip link set ovpnd-xyz down`
|
||||||
|
# - setting `wg-home` down *seems* to be not necessary
|
||||||
|
# - characterized by UPnP/SOAP error 500/718 in wireshark
|
||||||
|
# - seems it asks router A to open a port mapping for an IP address which belongs to a different subnet...
|
||||||
# - mic is sometimes disabled at call start despite presenting as enabled
|
# - mic is sometimes disabled at call start despite presenting as enabled
|
||||||
# - fix is to toggle it off -> on in the Dino UI
|
# - fix is to toggle it off -> on in the Dino UI
|
||||||
# - default mic gain is WAY TOO MUCH (heavily distorted)
|
# - default mic gain is WAY TOO MUCH (heavily distorted)
|
||||||
@@ -29,7 +34,7 @@
|
|||||||
# - possibly Dino should be updated internally: `info.rate / 100` -> `info.rate / 50`.
|
# - possibly Dino should be updated internally: `info.rate / 100` -> `info.rate / 50`.
|
||||||
# - i think that affects the batching for echo cancellation, adaptive gain control, etc.
|
# - i think that affects the batching for echo cancellation, adaptive gain control, etc.
|
||||||
#
|
#
|
||||||
{ config, lib, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.sane.programs.dino;
|
cfg = config.sane.programs.dino;
|
||||||
in
|
in
|
||||||
@@ -45,8 +50,25 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
packageUnwrapped = pkgs.dino.overrideAttrs (upstream: {
|
||||||
|
# i'm updating experimentally to see if it improves call performance.
|
||||||
|
# i don't *think* this is actually necessary; i don't notice any difference.
|
||||||
|
version = "0.4.3-unstable-2024-04-01";
|
||||||
|
src = lib.warnIf (lib.versionOlder "0.4.3" upstream.version) "dino update: safe to remove sane patches" pkgs.fetchFromGitHub {
|
||||||
|
owner = "dino";
|
||||||
|
repo = "dino";
|
||||||
|
rev = "d9fa4daa6a7d16f5f0e2183a77ee2d07849dd9f3";
|
||||||
|
hash = "sha256-vJBIMsMLlK8Aw19fD2aFNtegXkjOqEgb3m1hi3fE5DE=";
|
||||||
|
};
|
||||||
|
checkPhase = ''
|
||||||
|
runHook preCheck
|
||||||
|
./xmpp-vala-test
|
||||||
|
# ./signal-protocol-vala-test # doesn't exist anymore
|
||||||
|
runHook postCheck
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
sandbox.whitelistDbus = [ "user" ]; # notifications
|
sandbox.whitelistDbus = [ "user" ]; # notifications
|
||||||
@@ -69,26 +91,22 @@ in
|
|||||||
|
|
||||||
services.dino = {
|
services.dino = {
|
||||||
description = "dino XMPP client";
|
description = "dino XMPP client";
|
||||||
after = [ "graphical-session.target" ];
|
partOf = lib.mkIf cfg.config.autostart [ "graphical-session" ];
|
||||||
# partOf = [ "graphical-session.target" ];
|
|
||||||
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
|
|
||||||
|
|
||||||
serviceConfig = {
|
|
||||||
ExecStart = "${cfg.package}/bin/dino";
|
|
||||||
Type = "simple";
|
|
||||||
Restart = "always";
|
|
||||||
RestartSec = "20s";
|
|
||||||
};
|
|
||||||
|
|
||||||
# audio buffering; see: <https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#pipewire-buffering-explained>
|
# audio buffering; see: <https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#pipewire-buffering-explained>
|
||||||
# dino defaults to 10ms mic buffer, which causes underruns, which Dino handles *very* poorly
|
# dino defaults to 10ms mic buffer, which causes underruns, which Dino handles *very* poorly
|
||||||
# as in, the other end of the call will just not receive sound from us for a couple seconds.
|
# as in, the other end of the call will just not receive sound from us for a couple seconds.
|
||||||
# pipewire uses power-of-two buffering for the mic itself. that would put us at 21.33 ms, but this env var supports only whole numbers (21ms ends up not power-of-two).
|
# pipewire uses power-of-two buffering for the mic itself. that would put us at 21.33 ms, but this env var supports only whole numbers (21ms ends up not power-of-two).
|
||||||
# also, Dino's likely still doing things in 10ms batches internally anyway.
|
# also, Dino's likely still doing things in 10ms batches internally anyway.
|
||||||
environment.PULSE_LATENCY_MSEC = "20";
|
#
|
||||||
|
# further: decrease the "niceness" of dino, so that it can take precedence over anything else.
|
||||||
|
# ideally this would target just the audio processing, rather than the whole program.
|
||||||
|
# pipewire is the equivalent of `nice -n -21`, so probably don't want to go any more extreme than that.
|
||||||
|
# nice -n -15 chosen arbitrarily; not optimized
|
||||||
|
#
|
||||||
# note that debug logging during calls produces so much journal spam that it pegs the CPU and causes dropped audio
|
# note that debug logging during calls produces so much journal spam that it pegs the CPU and causes dropped audio
|
||||||
# environment.G_MESSAGES_DEBUG = "all";
|
# env G_MESSAGES_DEBUG = "all";
|
||||||
|
command = "env PULSE_LATENCY_MSEC=20 nice -n -15 dino";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -3,10 +3,10 @@
|
|||||||
# - notification sounds can be handled by swaync
|
# - notification sounds can be handled by swaync
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.sane.programs.gtkcord4;
|
cfg = config.sane.programs.dissent;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
sane.programs.gtkcord4 = {
|
sane.programs.dissent = {
|
||||||
configOption = with lib; mkOption {
|
configOption = with lib; mkOption {
|
||||||
default = {};
|
default = {};
|
||||||
type = types.submodule {
|
type = types.submodule {
|
||||||
@@ -17,22 +17,21 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
packageUnwrapped = pkgs.gtkcord4.overrideAttrs (upstream: {
|
packageUnwrapped = pkgs.dissent.overrideAttrs (upstream: {
|
||||||
postConfigure = (upstream.postConfigure or "") + ''
|
postConfigure = (upstream.postConfigure or "") + ''
|
||||||
# gtkcord4 uses go-keyring to interface with the org.freedesktop.secrets provider (i.e. gnome-keyring).
|
# dissent uses go-keyring to interface with the org.freedesktop.secrets provider (i.e. gnome-keyring).
|
||||||
# go-keyring hardcodes `login.keyring` as the keyring to store secrets in, instead of reading `~/.local/share/keyring/default`.
|
# go-keyring hardcodes `login.keyring` as the keyring to store secrets in, instead of reading `~/.local/share/keyring/default`.
|
||||||
# `login.keyring` seems to be a special keyring preconfigured (by gnome-keyring) to encrypt everything to the user's password.
|
# `login.keyring` seems to be a special keyring preconfigured (by gnome-keyring) to encrypt everything to the user's password.
|
||||||
# that's redundant with my fs-level encryption and makes the keyring less inspectable,
|
# that's redundant with my fs-level encryption and makes the keyring less inspectable,
|
||||||
# so patch gtkcord4 to use Default_keyring instead.
|
# so patch dissent to use Default_keyring instead.
|
||||||
# see:
|
# see:
|
||||||
# - <https://github.com/diamondburned/gtkcord4/issues/139>
|
# - <https://github.com/diamondburned/dissent/issues/139>
|
||||||
# - <https://github.com/zalando/go-keyring/issues/46>
|
# - <https://github.com/zalando/go-keyring/issues/46>
|
||||||
substituteInPlace vendor/github.com/zalando/go-keyring/secret_service/secret_service.go \
|
substituteInPlace vendor/github.com/zalando/go-keyring/secret_service/secret_service.go \
|
||||||
--replace '"login"' '"Default_keyring"'
|
--replace-fail '"login"' '"Default_keyring"'
|
||||||
'';
|
'';
|
||||||
});
|
});
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
sandbox.whitelistDbus = [ "user" ]; # notifications
|
sandbox.whitelistDbus = [ "user" ]; # notifications
|
||||||
@@ -52,22 +51,14 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
persist.byStore.private = [
|
persist.byStore.private = [
|
||||||
".cache/gtkcord4"
|
".cache/dissent"
|
||||||
".config/gtkcord4" # empty?
|
".config/dissent" # empty?
|
||||||
];
|
];
|
||||||
|
|
||||||
services.gtkcord4 = {
|
services.dissent = {
|
||||||
description = "gtkcord4 Discord client";
|
description = "dissent Discord client";
|
||||||
after = [ "graphical-session.target" ];
|
partOf = lib.mkIf cfg.config.autostart [ "graphical-session" ];
|
||||||
# partOf = [ "graphical-session.target" ];
|
command = "dissent";
|
||||||
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
|
|
||||||
|
|
||||||
serviceConfig = {
|
|
||||||
ExecStart = "${cfg.package}/bin/gtkcord4";
|
|
||||||
Type = "simple";
|
|
||||||
Restart = "always";
|
|
||||||
RestartSec = "20s";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
@@ -4,20 +4,28 @@
|
|||||||
# - <https://github.com/vector-im/element-desktop/issues/1029#issuecomment-1632688224>
|
# - <https://github.com/vector-im/element-desktop/issues/1029#issuecomment-1632688224>
|
||||||
# - `rm -rf ~/.config/Element/GPUCache`
|
# - `rm -rf ~/.config/Element/GPUCache`
|
||||||
# - <https://github.com/NixOS/nixpkgs/issues/244486>
|
# - <https://github.com/NixOS/nixpkgs/issues/244486>
|
||||||
{ pkgs, ... }:
|
{ lib, pkgs, ... }:
|
||||||
{
|
{
|
||||||
sane.programs.element-desktop = {
|
sane.programs.element-desktop = {
|
||||||
packageUnwrapped = pkgs.element-desktop.override {
|
packageUnwrapped = (pkgs.element-desktop.override {
|
||||||
# use pre-build electron because otherwise it takes 4 hrs to build from source.
|
# use pre-built electron because otherwise it takes 4 hrs to build from source.
|
||||||
electron = pkgs.electron-bin;
|
electron = pkgs.electron_28-bin;
|
||||||
};
|
}).overrideAttrs (upstream: {
|
||||||
|
# fix to use wayland instead of Xwayland:
|
||||||
|
# - replace `NIXOS_OZONE_WL` non-empty check with `WAYLAND_DISPLAY`
|
||||||
|
# - use `wayland` instead of `auto` because --ozone-platform-hint=auto still prefers X over wayland when both are available
|
||||||
|
# alternatively, set env var: `ELECTRON_OZONE_PLATFORM_HINT=wayland` and ignore all of this
|
||||||
|
installPhase = lib.replaceStrings
|
||||||
|
[ "NIXOS_OZONE_WL" "--ozone-platform-hint=auto" ]
|
||||||
|
[ "WAYLAND_DISPLAY" "--ozone-platform-hint=wayland" ]
|
||||||
|
upstream.installPhase
|
||||||
|
;
|
||||||
|
});
|
||||||
suggestedPrograms = [
|
suggestedPrograms = [
|
||||||
"gnome-keyring"
|
"gnome-keyring"
|
||||||
"xwayland"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
sandbox.whitelistDbus = [ "user" ]; # notifications
|
sandbox.whitelistDbus = [ "user" ]; # notifications
|
||||||
|
17
hosts/common/programs/engrampa.nix
Normal file
17
hosts/common/programs/engrampa.nix
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
sane.programs."mate.engrampa" = {
|
||||||
|
packageUnwrapped = pkgs.rmDbusServices pkgs.mate.engrampa;
|
||||||
|
sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
|
sandbox.whitelistWayland = true;
|
||||||
|
sandbox.autodetectCliPaths = "existingOrParent";
|
||||||
|
sandbox.extraHomePaths = [
|
||||||
|
"archive"
|
||||||
|
"Books/local"
|
||||||
|
"Books/servo"
|
||||||
|
"records"
|
||||||
|
"ref"
|
||||||
|
"tmp"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
@@ -12,11 +12,13 @@
|
|||||||
sandbox.wrapperType = "inplace"; # /share/epiphany/default-bookmarks.rdf refers back to /share; dbus files to /libexec
|
sandbox.wrapperType = "inplace"; # /share/epiphany/default-bookmarks.rdf refers back to /share; dbus files to /libexec
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
|
sandbox.whitelistDbus = [ "user" ]; #< silently fails to start without it.
|
||||||
# default sandboxing breaks rendering in weird ways. sites are super zoomed in / not scaled.
|
# default sandboxing breaks rendering in weird ways. sites are super zoomed in / not scaled.
|
||||||
# enabling DRI/DRM (as below) seems to fix that.
|
# enabling DRI/DRM (as below) seems to fix that.
|
||||||
sandbox.whitelistDri = true;
|
sandbox.whitelistDri = true;
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
sandbox.extraHomePaths = [
|
sandbox.extraHomePaths = [
|
||||||
|
".config/dconf" # else will always prompt "make default browser?"
|
||||||
".config/epiphany" #< else it gets angry at launch
|
".config/epiphany" #< else it gets angry at launch
|
||||||
"tmp"
|
"tmp"
|
||||||
];
|
];
|
||||||
|
113
hosts/common/programs/fcitx5.nix
Normal file
113
hosts/common/programs/fcitx5.nix
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# fcitx5 is an "input method", to e.g. allow typing CJK on qwerty.
|
||||||
|
# but i also misuse it to allow typing emoji on qwerty:
|
||||||
|
# - press `Super+backtick`
|
||||||
|
# - type something like "effort"
|
||||||
|
# - it should be underlined, at the least
|
||||||
|
# - if well supported (e.g. Firefox; also gtk4, alacritty on sway 1.10+), a drop-down fuzzy matcher will appear
|
||||||
|
# - press space
|
||||||
|
# - "effort" should be replaced by `(ง •̀_•́)ง`
|
||||||
|
#
|
||||||
|
## debugging
|
||||||
|
# - `fcitx5-diagnose`
|
||||||
|
#
|
||||||
|
## config/docs:
|
||||||
|
# - `fcitx5-configtool`, then check ~/.config/fcitx5 files
|
||||||
|
# - <https://fcitx-im.org/wiki/Fcitx_5>
|
||||||
|
# - <https://wiki.archlinux.org/title/Fcitx5>
|
||||||
|
# - theming: <https://wiki.archlinux.org/title/Fcitx5#Themes_and_appearance>
|
||||||
|
# - <https://en.wikipedia.org/wiki/Fcitx>
|
||||||
|
# - wayland specifics: <https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland>
|
||||||
|
# - quickphrase (emoji): <https://fcitx-im.org/wiki/QuickPhrase>
|
||||||
|
# - override phrases via `~/.config/fcitx/data/QuickPhrase.mb`
|
||||||
|
# - customize bindings via `fcitx5-configtool` > addons > QuickPhrase
|
||||||
|
# - theming:
|
||||||
|
# - nixpkgs has a few themes: `fcitx5-{material-color,nord,rose-pine}`
|
||||||
|
# - NUR has a few themes
|
||||||
|
# - <https://github.com/catppuccin/fcitx5>
|
||||||
|
{ lib, pkgs, ... }:
|
||||||
|
{
|
||||||
|
sane.programs.fcitx5 = {
|
||||||
|
packageUnwrapped = pkgs.fcitx5-with-addons.override {
|
||||||
|
addons = with pkgs; [
|
||||||
|
# fcitx5-mozc # japanese input: <https://github.com/fcitx/mozc>
|
||||||
|
fcitx5-gtk # <https://github.com/fcitx/fcitx5-gtk>
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
sandbox.method = "bwrap";
|
||||||
|
sandbox.whitelistDbus = [ "user" ];
|
||||||
|
sandbox.whitelistWayland = true; # for `fcitx5-configtool, if nothing else`
|
||||||
|
sandbox.extraHomePaths = [
|
||||||
|
# ".config/fcitx"
|
||||||
|
".config/fcitx5"
|
||||||
|
".local/share/fcitx5"
|
||||||
|
];
|
||||||
|
|
||||||
|
fs.".config/fcitx5/conf/quickphrase.conf".symlink.text = ''
|
||||||
|
# Choose key modifier
|
||||||
|
Choose Modifier=None
|
||||||
|
# Enable Spell check
|
||||||
|
Spell=True
|
||||||
|
FallbackSpellLanguage=en
|
||||||
|
|
||||||
|
[TriggerKey]
|
||||||
|
# defaults: Super+grave, Super+semicolon
|
||||||
|
# gtk apps use ctrl+period, so super+period is a nice complement
|
||||||
|
0=Super+grave
|
||||||
|
1=Super+semicolon
|
||||||
|
2=Super+period
|
||||||
|
'';
|
||||||
|
fs.".config/fcitx5/conf/classicui.conf".symlink.text = ''
|
||||||
|
Theme=sane
|
||||||
|
Font="Sans 20"
|
||||||
|
Vertical Candidate List=True
|
||||||
|
'';
|
||||||
|
fs.".local/share/fcitx5/themes/sane/theme.conf".symlink.text = ''
|
||||||
|
# i omit several keys, especially the ones which don't seem to do much.
|
||||||
|
# for a theme which uses many more options, see:
|
||||||
|
# - <https://github.com/catppuccin/fcitx5/blob/main/src/catppuccin-mocha/theme.conf>
|
||||||
|
[Metadata]
|
||||||
|
Name=sane
|
||||||
|
ScaleWithDPI=True
|
||||||
|
|
||||||
|
[InputPanel]
|
||||||
|
NormalColor=#d8d8d8
|
||||||
|
HighlightCandidateColor=#FFFFFF
|
||||||
|
HighlightColor=#FFFFFF
|
||||||
|
HighlightBackgroundColor=#1f5e54
|
||||||
|
|
||||||
|
[InputPanel/Background]
|
||||||
|
Color=#1f5e54
|
||||||
|
|
||||||
|
[InputPanel/Highlight]
|
||||||
|
Color=#418379
|
||||||
|
|
||||||
|
[InputPanel/Highlight/Margin]
|
||||||
|
Left=20
|
||||||
|
Right=20
|
||||||
|
Top=7
|
||||||
|
Bottom=7
|
||||||
|
|
||||||
|
[InputPanel/TextMargin]
|
||||||
|
Left=20
|
||||||
|
Right=20
|
||||||
|
Top=6
|
||||||
|
Bottom=6
|
||||||
|
'';
|
||||||
|
|
||||||
|
services.fcitx5 = {
|
||||||
|
description = "fcitx5: input method (IME) for emoji/internationalization";
|
||||||
|
partOf = [ "graphical-session" ];
|
||||||
|
command = "fcitx5";
|
||||||
|
};
|
||||||
|
|
||||||
|
env.XMODIFIERS = "@im=fcitx";
|
||||||
|
# setting IM_MODULE is generally not required on wayland, but can be used to override the toolkit's own dialogs with our own.
|
||||||
|
# env.GTK_IM_MODULE = "fcitx";
|
||||||
|
# enable if you want them:
|
||||||
|
# env.QT_IM_MODULE = "fcitx";
|
||||||
|
# env.QT_PLUGIN_PATH = [ "${cfg.package}/${pkgs.qt6.qtbase.qtPluginPrefix}" ];
|
||||||
|
# env.SDL_IM_MODULE = "fcitx";
|
||||||
|
# env.GLFW_IM_MODULE = "ibus"; # for KiTTY, as per <https://wiki.archlinux.org/title/Fcitx5#Integration>
|
||||||
|
};
|
||||||
|
}
|
@@ -25,7 +25,6 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.whitelistDbus = [ "user" ];
|
sandbox.whitelistDbus = [ "user" ];
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
|
|
||||||
@@ -97,18 +96,16 @@ in
|
|||||||
|
|
||||||
services.feedbackd = {
|
services.feedbackd = {
|
||||||
description = "feedbackd audio/vibration/led controller";
|
description = "feedbackd audio/vibration/led controller";
|
||||||
wantedBy = [ "default.target" ]; #< should technically be `sound.target`, but that doesn't seem to get auto-started?
|
depends = [ "sound" ];
|
||||||
serviceConfig = {
|
partOf = [ "default" ];
|
||||||
ExecStart = "${cfg.package}/libexec/feedbackd";
|
command = lib.concatStringsSep " " ([
|
||||||
Type = "simple";
|
"env"
|
||||||
Restart = "on-failure";
|
"G_MESSAGES_DEBUG=all"
|
||||||
RestartSec = "10s";
|
] ++ lib.optionals cfg.config.proxied [
|
||||||
};
|
"FEEDBACK_THEME=$HOME/.config/feedbackd/themes/proxied.json"
|
||||||
environment = {
|
] ++ [
|
||||||
G_MESSAGES_DEBUG = "all";
|
"${cfg.package}/libexec/feedbackd"
|
||||||
} // (lib.optionalAttrs cfg.config.proxied {
|
]);
|
||||||
FEEDBACK_THEME = "/home/colin/.config/feedbackd/themes/proxied.json";
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -234,7 +234,7 @@ in
|
|||||||
sane.programs.firefox = {
|
sane.programs.firefox = {
|
||||||
inherit packageUnwrapped;
|
inherit packageUnwrapped;
|
||||||
sandbox.method = "bwrap"; # landlock works, but requires all of /proc to be linked
|
sandbox.method = "bwrap"; # landlock works, but requires all of /proc to be linked
|
||||||
sandbox.wrapperType = "inplace"; # probably wrappedDerivation could work too.
|
sandbox.wrapperType = "inplace"; # trivial package; cheap enough to wrap inplace
|
||||||
sandbox.net = "all";
|
sandbox.net = "all";
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
sandbox.whitelistDbus = [ "user" ]; # mpris
|
sandbox.whitelistDbus = [ "user" ]; # mpris
|
||||||
@@ -322,6 +322,8 @@ in
|
|||||||
defaultPref("widget.use-xdg-desktop-portal.mime-handler", 1);
|
defaultPref("widget.use-xdg-desktop-portal.mime-handler", 1);
|
||||||
defaultPref("widget.use-xdg-desktop-portal.open-uri", 1);
|
defaultPref("widget.use-xdg-desktop-portal.open-uri", 1);
|
||||||
|
|
||||||
|
defaultPref("browser.toolbars.bookmarks.visibility", "never");
|
||||||
|
|
||||||
// auto-open mpv:// URIs without prompting.
|
// auto-open mpv:// URIs without prompting.
|
||||||
// can do this with other protocols too (e.g. matrix?). see about:config for common handlers.
|
// can do this with other protocols too (e.g. matrix?). see about:config for common handlers.
|
||||||
defaultPref("network.protocol-handler.external.mpv", true);
|
defaultPref("network.protocol-handler.external.mpv", true);
|
||||||
|
8
hosts/common/programs/firejail.nix
Normal file
8
hosts/common/programs/firejail.nix
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{ lib, config, ... }:
|
||||||
|
{
|
||||||
|
sane.programs.firejail = {};
|
||||||
|
|
||||||
|
programs.firejail = lib.mkIf config.sane.programs.firejail.enabled {
|
||||||
|
enable = true; #< install the suid binary
|
||||||
|
};
|
||||||
|
}
|
@@ -1,6 +1,10 @@
|
|||||||
# to preview fonts:
|
# to preview fonts:
|
||||||
# - `font-manager` (gui)
|
# - `font-manager` (gui)
|
||||||
# - useful to determine official name; codepoint support
|
# - useful to determine official name; codepoint support
|
||||||
|
# docs:
|
||||||
|
# - <https://slatecave.net/notebook/fontconfig/>
|
||||||
|
# debugging:
|
||||||
|
# - `fc-conflist` -> show all config files loaded
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
# nerdfonts takes popular open fonts and patches them to support a wider range of glyphs, notably emoji.
|
# nerdfonts takes popular open fonts and patches them to support a wider range of glyphs, notably emoji.
|
||||||
@@ -30,13 +34,12 @@ in
|
|||||||
{
|
{
|
||||||
sane.programs.fontconfig = {
|
sane.programs.fontconfig = {
|
||||||
sandbox.method = "bwrap"; # TODO:sandbox: untested
|
sandbox.method = "bwrap"; # TODO:sandbox: untested
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.autodetectCliPaths = "existingOrParent"; #< this might be overkill; or, how many programs reference fontconfig internally?
|
sandbox.autodetectCliPaths = "existingOrParent"; #< this might be overkill; or, how many programs reference fontconfig internally?
|
||||||
|
|
||||||
persist.byStore.plaintext = [
|
# persist.byStore.plaintext = [
|
||||||
# < 10 MiB
|
# # < 10 MiB. however, nixos generates its own fontconfig cache at build time now.
|
||||||
".cache/fontconfig"
|
# ".cache/fontconfig"
|
||||||
];
|
# ];
|
||||||
};
|
};
|
||||||
|
|
||||||
fonts = lib.mkIf config.sane.programs.fontconfig.enabled {
|
fonts = lib.mkIf config.sane.programs.fontconfig.enabled {
|
||||||
@@ -44,8 +47,8 @@ in
|
|||||||
fontconfig.defaultFonts = {
|
fontconfig.defaultFonts = {
|
||||||
emoji = [
|
emoji = [
|
||||||
"Noto Color Emoji"
|
"Noto Color Emoji"
|
||||||
"Font Awesome 6 Free"
|
# "Font Awesome 6 Free"
|
||||||
"Font Awesome 6 Brands"
|
# "Font Awesome 6 Brands"
|
||||||
];
|
];
|
||||||
monospace = [
|
monospace = [
|
||||||
"Hack Nerd Font Propo"
|
"Hack Nerd Font Propo"
|
||||||
@@ -67,7 +70,7 @@ in
|
|||||||
# TODO: reduce this font set.
|
# TODO: reduce this font set.
|
||||||
# - probably need only one of dejavu/freefont/liberation
|
# - probably need only one of dejavu/freefont/liberation
|
||||||
dejavu_fonts # 10 MiB; DejaVu {Sans,Serif,Sans Mono,Math TeX Gyre}; also available as a NerdFonts (Sans Mono only)
|
dejavu_fonts # 10 MiB; DejaVu {Sans,Serif,Sans Mono,Math TeX Gyre}; also available as a NerdFonts (Sans Mono only)
|
||||||
font-awesome # 2 MiB; Font Awesome 6 {Free,Brands}
|
# font-awesome # 2 MiB; Font Awesome 6 {Free,Brands}
|
||||||
freefont_ttf # 11 MiB; Free{Mono,Sans,Serif}
|
freefont_ttf # 11 MiB; Free{Mono,Sans,Serif}
|
||||||
gyre-fonts # 4 MiB; Tex Gyre *; ttf substitutes for standard PostScript fonts
|
gyre-fonts # 4 MiB; Tex Gyre *; ttf substitutes for standard PostScript fonts
|
||||||
# hack-font # 1 MiB; Hack; also available as a NerdFonts
|
# hack-font # 1 MiB; Hack; also available as a NerdFonts
|
||||||
|
@@ -28,7 +28,6 @@ in
|
|||||||
# packageUnwrapped = pkgs.fractal-next;
|
# packageUnwrapped = pkgs.fractal-next;
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
sandbox.whitelistDbus = [ "user" ]; # notifications
|
sandbox.whitelistDbus = [ "user" ]; # notifications
|
||||||
@@ -60,26 +59,19 @@ in
|
|||||||
|
|
||||||
persist.byStore.private = [
|
persist.byStore.private = [
|
||||||
# XXX by default fractal stores its state in ~/.local/share/<build-profile>/<UUID>.
|
# XXX by default fractal stores its state in ~/.local/share/<build-profile>/<UUID>.
|
||||||
".local/share/hack" # for debug-like builds
|
# ".local/share/hack" # for debug-like builds
|
||||||
".local/share/stable" # for normal releases
|
# ".local/share/stable" # for normal releases
|
||||||
".local/share/fractal" # for version 5+, i think?
|
".local/share/fractal" # for version 5+
|
||||||
];
|
];
|
||||||
|
|
||||||
suggestedPrograms = [ "gnome-keyring" ];
|
suggestedPrograms = [ "gnome-keyring" ];
|
||||||
|
|
||||||
services.fractal = {
|
services.fractal = {
|
||||||
description = "fractal Matrix client";
|
description = "fractal Matrix client";
|
||||||
after = [ "graphical-session.target" ];
|
partOf = lib.mkIf cfg.config.autostart [ "graphical-session" ];
|
||||||
# partOf = [ "graphical-session.target" ];
|
|
||||||
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
|
|
||||||
|
|
||||||
serviceConfig = {
|
# env "G_MESSAGES_DEBUG=all"
|
||||||
ExecStart = "${cfg.package}/bin/fractal";
|
command = "fractal";
|
||||||
Type = "simple";
|
|
||||||
Restart = "always";
|
|
||||||
RestartSec = "20s";
|
|
||||||
};
|
|
||||||
# environment.G_MESSAGES_DEBUG = "all";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,6 @@
|
|||||||
{
|
{
|
||||||
sane.programs.frozen-bubble = {
|
sane.programs.frozen-bubble = {
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.net = "clearnet"; # net play
|
sandbox.net = "clearnet"; # net play
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
{
|
{
|
||||||
|
sane.programs.fwupd = {};
|
||||||
services.fwupd = lib.mkIf config.sane.programs.fwupd.enabled {
|
services.fwupd = lib.mkIf config.sane.programs.fwupd.enabled {
|
||||||
# enables the dbus service, which i think the frontend speaks to.
|
# enables the dbus service, which i think the frontend speaks to.
|
||||||
enable = true;
|
enable = true;
|
||||||
|
@@ -9,7 +9,6 @@
|
|||||||
{
|
{
|
||||||
sane.programs.g4music = {
|
sane.programs.g4music = {
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.whitelistAudio = true;
|
sandbox.whitelistAudio = true;
|
||||||
sandbox.whitelistDbus = [ "user" ]; # mpris
|
sandbox.whitelistDbus = [ "user" ]; # mpris
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
|
@@ -4,7 +4,6 @@
|
|||||||
packageUnwrapped = pkgs.linkIntoOwnPackage pkgs.glib "bin/gdbus";
|
packageUnwrapped = pkgs.linkIntoOwnPackage pkgs.glib "bin/gdbus";
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.whitelistDbus = [ "user" ]; #< XXX: maybe future users will also want system access
|
sandbox.whitelistDbus = [ "user" ]; #< XXX: maybe future users will also want system access
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,6 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
sandbox.whitelistDbus = [ "user" ]; # notifications
|
sandbox.whitelistDbus = [ "user" ]; # notifications
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
@@ -88,16 +87,8 @@ in
|
|||||||
|
|
||||||
services.geary = {
|
services.geary = {
|
||||||
description = "geary email client";
|
description = "geary email client";
|
||||||
after = [ "graphical-session.target" ];
|
partOf = lib.mkIf cfg.config.autostart [ "graphical-session" ];
|
||||||
# partOf = [ "graphical-session.target" ];
|
command = "geary";
|
||||||
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
|
|
||||||
|
|
||||||
serviceConfig = {
|
|
||||||
ExecStart = "${cfg.package}/bin/geary";
|
|
||||||
Type = "simple";
|
|
||||||
Restart = "always";
|
|
||||||
RestartSec = "20s";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -19,7 +19,6 @@ in
|
|||||||
'';
|
'';
|
||||||
});
|
});
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
sandbox.whitelistPwd = true;
|
sandbox.whitelistPwd = true;
|
||||||
sandbox.autodetectCliPaths = true; # necessary for git-upload-pack
|
sandbox.autodetectCliPaths = true; # necessary for git-upload-pack
|
||||||
|
@@ -1,15 +1,12 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{ lib, pkgs, ... }:
|
||||||
let
|
|
||||||
cfg = config.sane.programs.gnome-keyring;
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
sane.programs.gnome-keyring = {
|
sane.programs.gnome-keyring = {
|
||||||
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-keyring;
|
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-keyring;
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.whitelistDbus = [ "user" ];
|
sandbox.whitelistDbus = [ "user" ];
|
||||||
sandbox.extraRuntimePaths = [
|
sandbox.extraRuntimePaths = [
|
||||||
"keyring/control"
|
"keyring" #< only needs keyring/control, but has to *create* that.
|
||||||
|
# "keyring/control"
|
||||||
];
|
];
|
||||||
sandbox.capabilities = [
|
sandbox.capabilities = [
|
||||||
# ipc_lock: used to `mlock` the secrets so they don't get swapped out.
|
# ipc_lock: used to `mlock` the secrets so they don't get swapped out.
|
||||||
@@ -28,10 +25,10 @@ in
|
|||||||
|
|
||||||
fs.".local/share/keyrings/default" = {
|
fs.".local/share/keyrings/default" = {
|
||||||
file.text = "Default_keyring.keyring"; #< no trailing newline
|
file.text = "Default_keyring.keyring"; #< no trailing newline
|
||||||
wantedBy = [ config.sane.fs."${config.sane.persist.stores.private.origin}".unit ];
|
# wantedBy = [ config.sane.fs."${config.sane.persist.stores.private.origin}".unit ];
|
||||||
wantedBeforeBy = [ #< don't create this as part of `multi-user.target`
|
# wantedBeforeBy = [ #< don't create this as part of `multi-user.target`
|
||||||
"gnome-keyring.service" # TODO: sane.programs should declare this dependency for us
|
# "gnome-keyring.service" # TODO: sane.programs should declare this dependency for us
|
||||||
];
|
# ];
|
||||||
};
|
};
|
||||||
# N.B.: certain keyring names have special significance
|
# N.B.: certain keyring names have special significance
|
||||||
# `login.keyring` is forcibly encrypted to the user's password, so that pam gnome-keyring can unlock it on login.
|
# `login.keyring` is forcibly encrypted to the user's password, so that pam gnome-keyring can unlock it on login.
|
||||||
@@ -43,22 +40,21 @@ in
|
|||||||
lock-on-idle=false
|
lock-on-idle=false
|
||||||
lock-after=false
|
lock-after=false
|
||||||
'';
|
'';
|
||||||
wantedBy = [ config.sane.fs."${config.sane.persist.stores.private.origin}".unit ];
|
# wantedBy = [ config.sane.fs."${config.sane.persist.stores.private.origin}".unit ];
|
||||||
wantedBeforeBy = [ #< don't create this as part of `multi-user.target`
|
# wantedBeforeBy = [ #< don't create this as part of `multi-user.target`
|
||||||
"gnome-keyring.service"
|
# "gnome-keyring.service"
|
||||||
];
|
# ];
|
||||||
};
|
};
|
||||||
|
|
||||||
services.gnome-keyring = {
|
services.gnome-keyring = {
|
||||||
description = "gnome-keyring-daemon: secret provider";
|
description = "gnome-keyring-daemon: secret provider";
|
||||||
after = [ "graphical-session.target" ];
|
partOf = [ "graphical-session" ];
|
||||||
wantedBy = [ "graphical-session.target" ];
|
command = let
|
||||||
serviceConfig = {
|
gkr-start = pkgs.writeShellScriptBin "gnome-keyring-daemon-start" ''
|
||||||
ExecStart = "${cfg.package}/bin/gnome-keyring-daemon --start --foreground --components=secrets";
|
mkdir -m 0700 -p $XDG_RUNTIME_DIR/keyring
|
||||||
Type = "simple";
|
exec gnome-keyring-daemon --start --foreground --components=secrets
|
||||||
Restart = "always";
|
'';
|
||||||
RestartSec = "20s";
|
in "${gkr-start}/bin/gnome-keyring-daemon-start";
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
{ ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
sane.programs."gnome.gnome-maps" = {
|
sane.programs."gnome.gnome-maps" = {
|
||||||
|
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-maps;
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "inplace"; #< dbus files
|
|
||||||
sandbox.whitelistDri = true; # for perf
|
sandbox.whitelistDri = true; # for perf
|
||||||
sandbox.whitelistDbus = [
|
sandbox.whitelistDbus = [
|
||||||
"system" # system is required for non-portal location services
|
"system" # system is required for non-portal location services
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
{
|
{
|
||||||
sane.programs."gnome.gnome-weather" = {
|
sane.programs."gnome.gnome-weather" = {
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "inplace";
|
sandbox.wrapperType = "inplace"; #< /share/org.gnome.Weather/org.gnome.Weather file refers to bins by full path
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
suggestedPrograms = [ "dconf" ]; #< stores city/location settings
|
suggestedPrograms = [ "dconf" ]; #< stores city/location settings
|
||||||
|
@@ -34,7 +34,6 @@ in
|
|||||||
{
|
{
|
||||||
sane.programs.go2tv = {
|
sane.programs.go2tv = {
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
sandbox.autodetectCliPaths = true;
|
sandbox.autodetectCliPaths = true;
|
||||||
# for GUI invocation, allow the common media directories
|
# for GUI invocation, allow the common media directories
|
||||||
|
@@ -23,7 +23,6 @@ in {
|
|||||||
});
|
});
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.whitelistDbus = [ "user" ]; # it won't launch without it, dunno exactly why.
|
sandbox.whitelistDbus = [ "user" ]; # it won't launch without it, dunno exactly why.
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
|
@@ -15,7 +15,6 @@
|
|||||||
"wl-clipboard"
|
"wl-clipboard"
|
||||||
];
|
];
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
sandbox.whitelistDbus = [ "user" ];
|
sandbox.whitelistDbus = [ "user" ];
|
||||||
sandbox.autodetectCliPaths = "existingFileOrParent";
|
sandbox.autodetectCliPaths = "existingFileOrParent";
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
{
|
{
|
||||||
sane.programs.handbrake = {
|
sane.programs.handbrake = {
|
||||||
sandbox.method = "landlock"; #< also supports bwrap, but landlock ensures we don't write to non-mounted tmpfs dir
|
sandbox.method = "landlock"; #< also supports bwrap, but landlock ensures we don't write to non-mounted tmpfs dir
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.whitelistDbus = [ "user" ]; # notifications
|
sandbox.whitelistDbus = [ "user" ]; # notifications
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
sandbox.extraHomePaths = [
|
sandbox.extraHomePaths = [
|
||||||
@@ -16,7 +15,7 @@
|
|||||||
|
|
||||||
# disable expensive sambda dependency; i don't use it.
|
# disable expensive sambda dependency; i don't use it.
|
||||||
packageUnwrapped = pkgs.handbrake.override {
|
packageUnwrapped = pkgs.handbrake.override {
|
||||||
ffmpeg_5-full = pkgs.ffmpeg_5-full.override {
|
ffmpeg-full = pkgs.ffmpeg-full.override {
|
||||||
withSamba = false;
|
withSamba = false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
11
hosts/common/programs/htop/default.nix
Normal file
11
hosts/common/programs/htop/default.nix
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
sane.programs.htop = {
|
||||||
|
sandbox.method = "landlock";
|
||||||
|
sandbox.extraPaths = [
|
||||||
|
"/proc"
|
||||||
|
"/sys/devices"
|
||||||
|
];
|
||||||
|
fs.".config/htop/htoprc".symlink.target = ./htoprc;
|
||||||
|
};
|
||||||
|
}
|
63
hosts/common/programs/htop/htoprc
Normal file
63
hosts/common/programs/htop/htoprc
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# Beware! This file is rewritten by htop when settings are changed in the interface.
|
||||||
|
# The parser is also very primitive, and not human-friendly.
|
||||||
|
htop_version=3.3.0
|
||||||
|
config_reader_min_version=3
|
||||||
|
fields=0 48 6 18 39 130 2 46 47 49 1
|
||||||
|
hide_kernel_threads=1
|
||||||
|
hide_userland_threads=0
|
||||||
|
hide_running_in_container=0
|
||||||
|
shadow_other_users=0
|
||||||
|
show_thread_names=0
|
||||||
|
show_program_path=0
|
||||||
|
highlight_base_name=0
|
||||||
|
highlight_deleted_exe=1
|
||||||
|
shadow_distribution_path_prefix=0
|
||||||
|
highlight_megabytes=1
|
||||||
|
highlight_threads=1
|
||||||
|
highlight_changes=0
|
||||||
|
highlight_changes_delay_secs=5
|
||||||
|
find_comm_in_cmdline=1
|
||||||
|
strip_exe_from_cmdline=1
|
||||||
|
show_merged_command=0
|
||||||
|
header_margin=1
|
||||||
|
screen_tabs=1
|
||||||
|
detailed_cpu_time=0
|
||||||
|
cpu_count_from_one=0
|
||||||
|
show_cpu_usage=1
|
||||||
|
show_cpu_frequency=0
|
||||||
|
show_cpu_temperature=0
|
||||||
|
degree_fahrenheit=0
|
||||||
|
update_process_names=0
|
||||||
|
account_guest_in_cpu_meter=0
|
||||||
|
color_scheme=0
|
||||||
|
enable_mouse=1
|
||||||
|
delay=15
|
||||||
|
hide_function_bar=0
|
||||||
|
header_layout=two_67_33
|
||||||
|
column_meters_0=AllCPUs Memory Swap Zram
|
||||||
|
column_meter_modes_0=1 1 1 1
|
||||||
|
column_meters_1=Systemd Uptime Tasks LoadAverage NetworkIO DiskIO
|
||||||
|
column_meter_modes_1=2 2 2 2 2 2
|
||||||
|
tree_view=0
|
||||||
|
sort_key=46
|
||||||
|
tree_sort_key=0
|
||||||
|
sort_direction=-1
|
||||||
|
tree_sort_direction=1
|
||||||
|
tree_view_always_by_pid=0
|
||||||
|
all_branches_collapsed=0
|
||||||
|
screen:Main=PID USER TTY NICE M_RESIDENT M_PRIV STATE PERCENT_CPU PERCENT_MEM TIME Command
|
||||||
|
.sort_key=PERCENT_CPU
|
||||||
|
.tree_sort_key=PID
|
||||||
|
.tree_view_always_by_pid=0
|
||||||
|
.tree_view=0
|
||||||
|
.sort_direction=-1
|
||||||
|
.tree_sort_direction=1
|
||||||
|
.all_branches_collapsed=0
|
||||||
|
screen:I/O=PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY Command
|
||||||
|
.sort_key=IO_RATE
|
||||||
|
.tree_sort_key=PID
|
||||||
|
.tree_view_always_by_pid=0
|
||||||
|
.tree_view=0
|
||||||
|
.sort_direction=-1
|
||||||
|
.tree_sort_direction=1
|
||||||
|
.all_branches_collapsed=0
|
@@ -2,7 +2,6 @@
|
|||||||
{
|
{
|
||||||
sane.programs.kdenlive = {
|
sane.programs.kdenlive = {
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.extraHomePaths = [
|
sandbox.extraHomePaths = [
|
||||||
"Music"
|
"Music"
|
||||||
"Pictures/from" # e.g. Videos taken from my phone
|
"Pictures/from" # e.g. Videos taken from my phone
|
||||||
|
@@ -11,7 +11,6 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
sandbox.method = "bwrap"; # TODO:sandbox untested
|
sandbox.method = "bwrap"; # TODO:sandbox untested
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
sandbox.whitelistDbus = [ "user" ]; # needs to connect to dconf via dbus
|
sandbox.whitelistDbus = [ "user" ]; # needs to connect to dconf via dbus
|
||||||
sandbox.whitelistDri = true; #< required
|
sandbox.whitelistDri = true; #< required
|
||||||
|
@@ -46,7 +46,6 @@ in {
|
|||||||
sane.programs.koreader = {
|
sane.programs.koreader = {
|
||||||
packageUnwrapped = pkgs.koreader-from-src;
|
packageUnwrapped = pkgs.koreader-from-src;
|
||||||
sandbox.method = "bwrap"; # sandboxes fine under landlock too, except for FTP
|
sandbox.method = "bwrap"; # sandboxes fine under landlock too, except for FTP
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
sandbox.whitelistDri = true; # reduces startup time and subjective page flip time
|
sandbox.whitelistDri = true; # reduces startup time and subjective page flip time
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
{
|
{
|
||||||
sane.programs.lemoa = {
|
sane.programs.lemoa = {
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.net = "clearnet";
|
sandbox.net = "clearnet";
|
||||||
sandbox.whitelistDbus = [ "user" ]; # for clicking links
|
sandbox.whitelistDbus = [ "user" ]; # for clicking links
|
||||||
sandbox.whitelistDri = true;
|
sandbox.whitelistDri = true;
|
||||||
|
8
hosts/common/programs/less.nix
Normal file
8
hosts/common/programs/less.nix
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
sane.programs.less = {
|
||||||
|
sandbox.method = "bwrap";
|
||||||
|
sandbox.autodetectCliPaths = "existingFile";
|
||||||
|
env.PAGER = "less";
|
||||||
|
};
|
||||||
|
}
|
@@ -12,7 +12,6 @@
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
sandbox.autodetectCliPaths = "parent";
|
sandbox.autodetectCliPaths = "parent";
|
||||||
sandbox.extraHomePaths = [
|
sandbox.extraHomePaths = [
|
||||||
|
@@ -53,13 +53,8 @@
|
|||||||
# on environment.packages, but then logs are blackholed.
|
# on environment.packages, but then logs are blackholed.
|
||||||
services.mako = {
|
services.mako = {
|
||||||
description = "mako desktop notification daemon";
|
description = "mako desktop notification daemon";
|
||||||
wantedBy = [ "graphical-session.target" ];
|
partOf = [ "graphical-session" ];
|
||||||
|
command = "${config.sane.programs.mako.package}/bin/mako";
|
||||||
serviceConfig.ExecStart = "${config.sane.programs.mako.package}/bin/mako";
|
|
||||||
serviceConfig.Type = "simple";
|
|
||||||
# mako will predictably fail if launched before the wayland server is fully initialized
|
|
||||||
serviceConfig.Restart = "on-failure";
|
|
||||||
serviceConfig.RestartSec = "10s";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -10,19 +10,13 @@
|
|||||||
# bwrap (loupe image viewer) doesn't like to run inside landlock
|
# bwrap (loupe image viewer) doesn't like to run inside landlock
|
||||||
# "bwrap: failed to make / slave: Operation not permitted"
|
# "bwrap: failed to make / slave: Operation not permitted"
|
||||||
sandbox.method = "bwrap"; # supports landlock or bwrap
|
sandbox.method = "bwrap"; # supports landlock or bwrap
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.whitelistDri = true;
|
sandbox.whitelistDri = true;
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
sandbox.whitelistDbus = [ "user" ]; #< so that it can in theory open the image viewer using fdo portal... but it doesn't :|
|
sandbox.whitelistDbus = [ "user" ]; #< so that it can in theory open the image viewer using fdo portal... but it doesn't :|
|
||||||
sandbox.extraHomePaths = [
|
sandbox.extraHomePaths = [
|
||||||
|
".config/dconf" #< else it segfaults during post-process
|
||||||
# ".config/megapixels"
|
# ".config/megapixels"
|
||||||
# ".config/xcb"
|
|
||||||
# ".xcb"
|
|
||||||
".local/share/applications" #< needed for viewing photos, until i can sort out the portal stuff
|
".local/share/applications" #< needed for viewing photos, until i can sort out the portal stuff
|
||||||
# ".local/share/icons"
|
|
||||||
# ".icons" #< actually needed!
|
|
||||||
# ".themes"
|
|
||||||
# ".nix-profile"
|
|
||||||
".cache/mesa_shader_cache" # loads way faster
|
".cache/mesa_shader_cache" # loads way faster
|
||||||
"tmp"
|
"tmp"
|
||||||
"Pictures" #< TODO: make this Pictures/Photos and save photos there
|
"Pictures" #< TODO: make this Pictures/Photos and save photos there
|
||||||
|
@@ -5,7 +5,6 @@
|
|||||||
{
|
{
|
||||||
sane.programs.mepo = {
|
sane.programs.mepo = {
|
||||||
sandbox.method = "bwrap";
|
sandbox.method = "bwrap";
|
||||||
sandbox.wrapperType = "wrappedDerivation";
|
|
||||||
sandbox.net = "all"; # for tiles *and* for localhost comm to gpsd
|
sandbox.net = "all"; # for tiles *and* for localhost comm to gpsd
|
||||||
sandbox.whitelistDri = true;
|
sandbox.whitelistDri = true;
|
||||||
sandbox.whitelistWayland = true;
|
sandbox.whitelistWayland = true;
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
# mimeo is an exec dispatcher like xdg-open, but why allows mapping different URL regexes to different handlers.
|
# mimeo is an exec dispatcher like xdg-open, but which allows mapping different URL regexes to different handlers.
|
||||||
# my setup sets mimeo as the default http/https handler,
|
# my setup sets mimeo as the default http/https handler,
|
||||||
# and from there it dispatches specialized rules, falling back to the original http/https handler if no URL specialization exists
|
# and from there it dispatches specialized rules, falling back to the original http/https handler if no URL specialization exists
|
||||||
|
#
|
||||||
|
# alternative to mimeo is jaro: <https://github.com/isamert/jaro>
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
mimeo-open-desktop = pkgs.static-nix-shell.mkPython3Bin {
|
mimeo-open-desktop = pkgs.static-nix-shell.mkPython3Bin {
|
||||||
|
@@ -1,195 +0,0 @@
|
|||||||
# mpv docs:
|
|
||||||
# - <https://mpv.io/manual/master>
|
|
||||||
# - <https://github.com/mpv-player/mpv/wiki>
|
|
||||||
# curated mpv mods/scripts/users:
|
|
||||||
# - <https://github.com/stax76/awesome-mpv>
|
|
||||||
{ config, lib, pkgs, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.sane.programs.mpv;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
sane.programs.mpv = {
|
|
||||||
configOption = with lib; mkOption {
|
|
||||||
default = {};
|
|
||||||
type = types.submodule {
|
|
||||||
options.vo = mkOption {
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
description = "--vo=FOO flag to pass to mpv";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
packageUnwrapped = (pkgs.wrapMpv pkgs.mpv-unwrapped {
|
|
||||||
scripts = with pkgs.mpvScripts; [
|
|
||||||
mpris
|
|
||||||
uosc
|
|
||||||
# pkgs.mpv-uosc-latest
|
|
||||||
];
|
|
||||||
extraMakeWrapperArgs = lib.optionals (cfg.config.vo != null) [
|
|
||||||
# 2023/08/29: fixes an error where mpv on moby launches with the message
|
|
||||||
# "DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory"
|
|
||||||
# audio still works, and controls, screenshotting, etc -- just not the actual rendering
|
|
||||||
#
|
|
||||||
# this is likely a regression for mpv 0.36.0.
|
|
||||||
# the actual error message *appears* to come from the mesa library, but it's tough to trace.
|
|
||||||
#
|
|
||||||
# TODO(2023/12/03): remove once mesa 23.3.1 lands: <https://github.com/NixOS/nixpkgs/pull/265740>
|
|
||||||
#
|
|
||||||
# backend compatibility (2023/10/22):
|
|
||||||
# run with `--vo=help` to see a list of all output options.
|
|
||||||
# non-exhaustive (W=works, F=fails, A=audio-only, U=audio+ui only (no video))
|
|
||||||
# ? null Null video output
|
|
||||||
# A (default)
|
|
||||||
# A dmabuf-wayland Wayland dmabuf video output
|
|
||||||
# A libmpv render API for libmpv (mpv plays the audio, but doesn't even render a window)
|
|
||||||
# A vdpau VDPAU with X11
|
|
||||||
# F drm Direct Rendering Manager (software scaling)
|
|
||||||
# F gpu-next Video output based on libplacebo
|
|
||||||
# F vaapi VA API with X11
|
|
||||||
# F x11 X11 (software scaling)
|
|
||||||
# F xv X11/Xv
|
|
||||||
# U gpu Shader-based GPU Renderer
|
|
||||||
# W caca libcaca (terminal rendering)
|
|
||||||
# W sdl SDL 2.0 Renderer
|
|
||||||
# W wlshm Wayland SHM video output (software scaling)
|
|
||||||
"--add-flags" "--vo=${cfg.config.vo}"
|
|
||||||
];
|
|
||||||
}).overrideAttrs (base: {
|
|
||||||
buildCommand = base.buildCommand + ''
|
|
||||||
# runHook postFixup to allow sandbox wrappers to wrap the binaries
|
|
||||||
runHook postFixup
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
|
|
||||||
sandbox.method = "bwrap";
|
|
||||||
sandbox.autodetectCliPaths = true;
|
|
||||||
sandbox.net = "all";
|
|
||||||
sandbox.whitelistAudio = true;
|
|
||||||
sandbox.whitelistDbus = [ "user" ]; #< mpris
|
|
||||||
sandbox.whitelistDri = true; #< mpv has excellent fallbacks to non-DRI, but DRI offers a good 30%-50% reduced CPU
|
|
||||||
sandbox.whitelistWayland = true;
|
|
||||||
sandbox.extraHomePaths = [
|
|
||||||
".config/mpv" #< else mpris plugin crashes on launch
|
|
||||||
# it's common for album (or audiobook, podcast) images/lyrics/metadata to live adjacent to the primary file.
|
|
||||||
# CLI detection is too poor to pick those up, so expose the common media dirs to the sandbox to make that *mostly* work.
|
|
||||||
"Books/local"
|
|
||||||
"Books/servo"
|
|
||||||
"Music"
|
|
||||||
"Videos/local"
|
|
||||||
"Videos/servo"
|
|
||||||
];
|
|
||||||
|
|
||||||
persist.byStore.plaintext = [
|
|
||||||
# for `watch_later`
|
|
||||||
".local/state/mpv"
|
|
||||||
];
|
|
||||||
fs.".config/mpv/input.conf".symlink.text = let
|
|
||||||
execInTerm = "${pkgs.xdg-terminal-exec}/bin/xdg-terminal-exec";
|
|
||||||
in ''
|
|
||||||
# docs:
|
|
||||||
# - <https://mpv.io/manual/master/#list-of-input-commands>
|
|
||||||
# - script-binding: <https://mpv.io/manual/master/#command-interface-script-binding>
|
|
||||||
# - properties: <https://mpv.io/manual/master/#property-list>
|
|
||||||
|
|
||||||
# let volume/power keys be interpreted by the system.
|
|
||||||
# this is important for sxmo.
|
|
||||||
# mpv defaults is POWER = close, VOLUME_{UP,DOWN} = adjust application-level volume
|
|
||||||
POWER ignore
|
|
||||||
VOLUME_UP ignore
|
|
||||||
VOLUME_DOWN ignore
|
|
||||||
|
|
||||||
# uosc menu
|
|
||||||
# text after the shebang is parsed by uosc to construct the menu and names
|
|
||||||
menu script-binding uosc/menu
|
|
||||||
s script-binding uosc/subtitles #! Subtitles
|
|
||||||
a script-binding uosc/audio #! Audio tracks
|
|
||||||
q script-binding uosc/stream-quality #! Stream quality
|
|
||||||
p script-binding uosc/items #! Playlist
|
|
||||||
c script-binding uosc/chapters #! Chapters
|
|
||||||
> script-binding uosc/next #! Navigation > Next
|
|
||||||
< script-binding uosc/prev #! Navigation > Prev
|
|
||||||
o script-binding uosc/open-file #! Navigation > Open file
|
|
||||||
# set video-aspect-override "-1" #! Utils > Aspect ratio > Default
|
|
||||||
# set video-aspect-override "16:9" #! Utils > Aspect ratio > 16:9
|
|
||||||
# set video-aspect-override "4:3" #! Utils > Aspect ratio > 4:3
|
|
||||||
# set video-aspect-override "2.35:1" #! Utils > Aspect ratio > 2.35:1
|
|
||||||
# script-binding uosc/audio-device #! Utils > Audio devices
|
|
||||||
# script-binding uosc/editions #! Utils > Editions
|
|
||||||
ctrl+s async screenshot #! Utils > Screenshot
|
|
||||||
alt+i script-binding uosc/keybinds #! Utils > Key bindings
|
|
||||||
O script-binding uosc/show-in-directory #! Utils > Show in directory
|
|
||||||
# script-binding uosc/open-config-directory #! Utils > Open config directory
|
|
||||||
# set pause yes; run ${execInTerm} go2tv -v "''${stream-open-filename}" #! Cast
|
|
||||||
# set pause yes; run ${execInTerm} go2tv -u "''${stream-open-filename}" #! Cast (...) > Stream
|
|
||||||
# set pause yes; run go2tv #! Cast (...) > GUI
|
|
||||||
# TODO: unify "Cast" and "Cast (stream)" options above.
|
|
||||||
'';
|
|
||||||
fs.".config/mpv/mpv.conf".symlink.text = ''
|
|
||||||
save-position-on-quit=yes
|
|
||||||
keep-open=yes
|
|
||||||
|
|
||||||
# force GUI, even for tracks w/o album art
|
|
||||||
# see: <https://www.reddit.com/r/mpv/comments/rvrrpt/oscosdgui_and_arch_linux/>
|
|
||||||
player-operation-mode=pseudo-gui
|
|
||||||
|
|
||||||
# use uosc instead (for On Screen Controls)
|
|
||||||
osc=no
|
|
||||||
# uosc provides its own seeking/volume indicators, so you also don't need this
|
|
||||||
osd-bar=no
|
|
||||||
# uosc will draw its own window controls if you disable window border
|
|
||||||
border=no
|
|
||||||
'';
|
|
||||||
fs.".config/mpv/script-opts/osc.conf".symlink.text = ''
|
|
||||||
# make the on-screen controls *always* visible
|
|
||||||
# unfortunately, this applies to full-screen as well
|
|
||||||
# - docs: <https://mpv.io/manual/master/#on-screen-controller-visibility>
|
|
||||||
# if uosc is installed, this file is unused
|
|
||||||
visibility=always
|
|
||||||
'';
|
|
||||||
fs.".config/mpv/script-opts/uosc.conf".symlink.text = let
|
|
||||||
play_pause_btn = "cycle:play_arrow:pause:no=pause/yes=play_arrow";
|
|
||||||
rev_btn = "command:replay_10:seek -10";
|
|
||||||
fwd_btn = "command:forward_30:seek 30";
|
|
||||||
in ''
|
|
||||||
# docs:
|
|
||||||
# - <https://github.com/tomasklaen/uosc>
|
|
||||||
# - <https://github.com/tomasklaen/uosc/blob/main/src/uosc.conf>
|
|
||||||
# - <https://superuser.com/questions/1775550/add-new-buttons-to-mpv-uosc-ui>
|
|
||||||
timeline_style=bar
|
|
||||||
timeline_persistency=paused,audio
|
|
||||||
controls_persistency=paused,audio
|
|
||||||
volume_persistency=audio
|
|
||||||
volume_opacity=0.75
|
|
||||||
|
|
||||||
# speed_persistency=paused,audio
|
|
||||||
# vvv want a close button?
|
|
||||||
top_bar=always
|
|
||||||
top_bar_persistency=paused
|
|
||||||
|
|
||||||
controls=menu,<video>subtitles,<has_many_audio>audio,<has_many_video>video,<has_many_edition>editions,<stream>stream-quality,space,${rev_btn},${play_pause_btn},${fwd_btn},space,speed:1.0,gap,<video>fullscreen
|
|
||||||
|
|
||||||
text_border=6.0
|
|
||||||
font_bold=yes
|
|
||||||
color=foreground=ff8080,background_text=ff8080
|
|
||||||
|
|
||||||
ui_scale=1.0
|
|
||||||
'';
|
|
||||||
|
|
||||||
# mime.priority = 200; # default = 100; 200 means to yield to other apps
|
|
||||||
mime.priority = 50; # default = 100; 50 in order to take precedence over vlc.
|
|
||||||
mime.associations."audio/flac" = "mpv.desktop";
|
|
||||||
mime.associations."audio/mpeg" = "mpv.desktop";
|
|
||||||
mime.associations."audio/x-opus+ogg" = "mpv.desktop";
|
|
||||||
mime.associations."audio/x-vorbis+ogg" = "mpv.desktop";
|
|
||||||
mime.associations."video/mp4" = "mpv.desktop";
|
|
||||||
mime.associations."video/quicktime" = "mpv.desktop";
|
|
||||||
mime.associations."video/webm" = "mpv.desktop";
|
|
||||||
mime.associations."video/x-flv" = "mpv.desktop";
|
|
||||||
mime.associations."video/x-matroska" = "mpv.desktop";
|
|
||||||
mime.urlAssociations."^https?://(www.)?youtube.com/watch\?.*v=" = "mpv.desktop";
|
|
||||||
mime.urlAssociations."^https?://(www.)?youtube.com/v/" = "mpv.desktop";
|
|
||||||
mime.urlAssociations."^https?://(www.)?youtu.be/.+" = "mpv.desktop";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
3
hosts/common/programs/mpv/console.conf
Normal file
3
hosts/common/programs/mpv/console.conf
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# font size used by mpv's console (`); default 16
|
||||||
|
# font_size=28
|
||||||
|
scale=2
|
219
hosts/common/programs/mpv/default.nix
Normal file
219
hosts/common/programs/mpv/default.nix
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
# curated mpv mods/scripts/users:
|
||||||
|
# - <https://github.com/stax76/awesome-mpv>
|
||||||
|
# mpv docs:
|
||||||
|
# - <https://mpv.io/manual/master>
|
||||||
|
# - <https://github.com/mpv-player/mpv/wiki>
|
||||||
|
# extensions i use:
|
||||||
|
# - <https://github.com/jonniek/mpv-playlistmanager>
|
||||||
|
# other extensions that could be useful:
|
||||||
|
# - list: <https://github.com/stax76/awesome-mpv>
|
||||||
|
# - list: <https://nudin.github.io/mpv-script-directory/>
|
||||||
|
# - browse DLNA shares: <https://github.com/chachmu/mpvDLNA>
|
||||||
|
# - act as a DLNA renderer (sink): <https://github.com/xfangfang/Macast>
|
||||||
|
# - update watch_later periodically -- not just on exit: <https://gist.github.com/CyberShadow/2f71a97fb85ed42146f6d9f522bc34ef>
|
||||||
|
# - <https://github.com/AN3223/dotfiles/blob/master/.config/mpv/scripts/auto-save-state.lua>
|
||||||
|
# - touch shortcuts (double-tap L/R portions of window to seek, etc): <https://github.com/christoph-heinrich/mpv-touch-gestures>
|
||||||
|
# - <https://github.com/omeryagmurlu/mpv-gestures>
|
||||||
|
# - jellyfin client: <https://github.com/EmperorPenguin18/mpv-jellyfin>
|
||||||
|
# - DLNA client (player only: no casting): <https://github.com/chachmu/mpvDLNA>
|
||||||
|
# - search videos on Youtube: <https://github.com/rozari0/mpv-youtube-search>
|
||||||
|
# - <https://github.com/CogentRedTester/mpv-scripts/blob/master/youtube-search.lua>
|
||||||
|
# - sponsorblock: <https://codeberg.org/jouni/mpv_sponsorblock_minimal>
|
||||||
|
# - screenshot-to-clipboard: <https://github.com/zc62/mpv-scripts/blob/master/screenshot-to-clipboard.js>
|
||||||
|
# - mpv-as-image-viewer: <https://github.com/guidocella/mpv-image-config>
|
||||||
|
# debugging:
|
||||||
|
# - enter console by pressing backtick.
|
||||||
|
# > `set volume 50` -> sets application volume to 50%
|
||||||
|
# > `set ao-volume 50` -> sets system-wide volume to 50%
|
||||||
|
# > `show-text "vol: ${volume}"` -> get the volume
|
||||||
|
# - show script output by running mpv with `--msg-level=all=trace`
|
||||||
|
# - and then just `print(...)` from lua & it'll show in terminal
|
||||||
|
# - requires that mpv.conf NOT include player-operation-mode=pseudo-gui
|
||||||
|
# - invoke mpv with `--no-config` to have it not read ~/.config/mpv/*
|
||||||
|
# - press `i` to show decoder info
|
||||||
|
#
|
||||||
|
# usage tips:
|
||||||
|
# - `<` or `>` to navigate prev/next-file-in-folder (uosc)
|
||||||
|
# - shift+enter to view the playlist, then arrow-keys to navigate (mpv-playlistmanager)
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.sane.programs.mpv;
|
||||||
|
uosc = pkgs.mpvScripts.uosc.overrideAttrs (upstream: {
|
||||||
|
version = "5.2.0-unstable-2024-03-13";
|
||||||
|
src = lib.warnIf (lib.versionOlder "5.2.0" upstream.version) "uosc outdated; remove patch?" pkgs.fetchFromGitHub {
|
||||||
|
owner = "tomasklaen";
|
||||||
|
repo = "uosc";
|
||||||
|
rev = "6fa34c31d0a5290dee83282205768d15111df7d8";
|
||||||
|
hash = "sha256-qxyNZHmH33bKRp4heFSC+RtvSApIfbVFt4otfS351nE=";
|
||||||
|
};
|
||||||
|
# src = pkgs.fetchFromGitea {
|
||||||
|
# domain = "git.uninsane.org";
|
||||||
|
# owner = "colin";
|
||||||
|
# repo = "uosc";
|
||||||
|
# rev = "dev-sane-5.2.0";
|
||||||
|
# hash = "sha256-lpqk4nnCxDZr/Y7/seM4VyR30fVrDAT4VP7C8n88lvA=";
|
||||||
|
# };
|
||||||
|
|
||||||
|
postPatch = (upstream.postPatch or "") + ''
|
||||||
|
### patch so touch controls work well with sway 1.9+
|
||||||
|
### in particular, "mouse.hover" is *always* false for touch events (i guess this is a bug in mpv?)
|
||||||
|
### and a touch release event is always followed by a mouse move to the cursor (that's a sway thing) which doesn't make sense.
|
||||||
|
# 1. always listen for mbtn_left events, even before a hover event would activate a zone:
|
||||||
|
substituteInPlace src/uosc/lib/cursor.lua \
|
||||||
|
--replace-fail \
|
||||||
|
"if binding and cursor:collides_with(zone.hitbox)" \
|
||||||
|
"if binding"
|
||||||
|
# 2. uosc already simulates mouse movements on touch down, but because of the hover handling, they get misunderstood as mouse leaves.
|
||||||
|
# so, bypass the cursor:leave() check.
|
||||||
|
substituteInPlace src/uosc/lib/cursor.lua \
|
||||||
|
--replace-fail \
|
||||||
|
"handle_mouse_pos(nil, mp.get_property_native('mouse-pos'))" \
|
||||||
|
"local mpos = mp.get_property_native('mouse-pos')
|
||||||
|
cursor:move(mpos.x, mpos.y)
|
||||||
|
cursor.hover_raw = mpos.hover"
|
||||||
|
# 3. explicitly fire a cursor:leave on touch release, so that all zones are deactivated (and control visibility goes back to default state)
|
||||||
|
substituteInPlace src/uosc/lib/cursor.lua \
|
||||||
|
--replace-fail \
|
||||||
|
"cursor:create_handler('primary_up')" \
|
||||||
|
"function(...)
|
||||||
|
cursor:trigger('primary_up', ...)
|
||||||
|
if not cursor.hover_raw then
|
||||||
|
cursor:leave()
|
||||||
|
end
|
||||||
|
end"
|
||||||
|
# 4. sometimes we get a touch movement shortly AFTER touch is released:
|
||||||
|
# detect that and ignore it
|
||||||
|
substituteInPlace src/uosc/lib/cursor.lua \
|
||||||
|
--replace-fail \
|
||||||
|
"cursor:move(mouse.x, mouse.y)" \
|
||||||
|
"local last_down = cursor.last_event['primary_down'] or { time = 0 }
|
||||||
|
local last_up = cursor.last_event['primary_up'] or { time = 0 }
|
||||||
|
if cursor.hover_raw or last_down.time >= last_up.time then cursor:move(mouse.x, mouse.y) end"
|
||||||
|
|
||||||
|
### patch so that uosc volume control is routed to sane-sysvol.
|
||||||
|
### this is particularly nice for moby, because it avoids the awkwardness that system volume
|
||||||
|
### is hard to adjust while screen is on.
|
||||||
|
### previously i used ao-volume instead of sane-sysvol: but that forced `ao=alsa`
|
||||||
|
### and came with heavy perf penalties (especially when adjusting the volume)
|
||||||
|
substituteInPlace src/uosc/main.lua \
|
||||||
|
--replace-fail \
|
||||||
|
"mp.observe_property('volume'" \
|
||||||
|
"mp.observe_property('user-data/sane-sysvol/volume'"
|
||||||
|
substituteInPlace src/uosc/elements/Volume.lua \
|
||||||
|
--replace-fail \
|
||||||
|
"mp.commandv('set', 'volume'" \
|
||||||
|
"mp.set_property_native('user-data/sane-sysvol/volume'" \
|
||||||
|
--replace-fail \
|
||||||
|
"mp.set_property_native('volume'" \
|
||||||
|
"mp.set_property_native('user-data/sane-sysvol/volume'"
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
mpv-unwrapped = pkgs.mpv-unwrapped.overrideAttrs (upstream: {
|
||||||
|
version = "0.37.0-unstable-2024-03-31";
|
||||||
|
src = lib.warnIf (lib.versionOlder "0.37.0" upstream.version) "mpv outdated; remove patch?" pkgs.fetchFromGitHub {
|
||||||
|
owner = "mpv-player";
|
||||||
|
repo = "mpv";
|
||||||
|
rev = "4ce4bf1795e6dfd6f1ddf07fb348ce5d191ab1dc";
|
||||||
|
hash = "sha256-nOGuHq7SWDAygROV7qHtezDv1AsMpseImI8TVd3F+Oc=";
|
||||||
|
};
|
||||||
|
patches = [];
|
||||||
|
});
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sane.programs.mpv = {
|
||||||
|
packageUnwrapped = pkgs.wrapMpv (mpv-unwrapped.override { lua = pkgs.luajit; }) {
|
||||||
|
scripts = [
|
||||||
|
pkgs.mpvScripts.mpris
|
||||||
|
pkgs.mpvScripts.mpv-playlistmanager
|
||||||
|
uosc
|
||||||
|
# pkgs.mpv-uosc-latest
|
||||||
|
];
|
||||||
|
# extraMakeWrapperArgs = lib.optionals (cfg.config.vo != null) [
|
||||||
|
# # 2023/08/29: fixes an error where mpv on moby launches with the message
|
||||||
|
# # "DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory"
|
||||||
|
# # audio still works, and controls, screenshotting, etc -- just not the actual rendering
|
||||||
|
# #
|
||||||
|
# # this is likely a regression for mpv 0.36.0.
|
||||||
|
# # the actual error message *appears* to come from the mesa library, but it's tough to trace.
|
||||||
|
# #
|
||||||
|
# # 2024/03/02: no longer necessary, with mesa 23.3.1: <https://github.com/NixOS/nixpkgs/pull/265740>
|
||||||
|
# #
|
||||||
|
# # backend compatibility (2023/10/22):
|
||||||
|
# # run with `--vo=help` to see a list of all output options.
|
||||||
|
# # non-exhaustive (W=works, F=fails, A=audio-only, U=audio+ui only (no video))
|
||||||
|
# # ? null Null video output
|
||||||
|
# # A (default)
|
||||||
|
# # A dmabuf-wayland Wayland dmabuf video output
|
||||||
|
# # A libmpv render API for libmpv (mpv plays the audio, but doesn't even render a window)
|
||||||
|
# # A vdpau VDPAU with X11
|
||||||
|
# # F drm Direct Rendering Manager (software scaling)
|
||||||
|
# # F gpu-next Video output based on libplacebo
|
||||||
|
# # F vaapi VA API with X11
|
||||||
|
# # F x11 X11 (software scaling)
|
||||||
|
# # F xv X11/Xv
|
||||||
|
# # U gpu Shader-based GPU Renderer
|
||||||
|
# # W caca libcaca (terminal rendering)
|
||||||
|
# # W sdl SDL 2.0 Renderer
|
||||||
|
# # W wlshm Wayland SHM video output (software scaling)
|
||||||
|
# "--add-flags" "--vo=${cfg.config.vo}"
|
||||||
|
# ];
|
||||||
|
};
|
||||||
|
|
||||||
|
suggestedPrograms = [
|
||||||
|
"blast-to-default"
|
||||||
|
"go2tv"
|
||||||
|
"xdg-terminal-exec"
|
||||||
|
];
|
||||||
|
|
||||||
|
sandbox.method = "bwrap";
|
||||||
|
sandbox.autodetectCliPaths = true;
|
||||||
|
sandbox.net = "all";
|
||||||
|
sandbox.whitelistAudio = true;
|
||||||
|
sandbox.whitelistDbus = [ "user" ]; #< mpris
|
||||||
|
sandbox.whitelistDri = true; #< mpv has excellent fallbacks to non-DRI, but DRI offers a good 30%-50% reduced CPU
|
||||||
|
sandbox.whitelistWayland = true;
|
||||||
|
sandbox.extraHomePaths = [
|
||||||
|
".config/mpv" #< else mpris plugin crashes on launch
|
||||||
|
".local/share/applications" #< for xdg-terminal-exec (go2tv)
|
||||||
|
# it's common for album (or audiobook, podcast) images/lyrics/metadata to live adjacent to the primary file.
|
||||||
|
# CLI detection is too poor to pick those up, so expose the common media dirs to the sandbox to make that *mostly* work.
|
||||||
|
"Books/local"
|
||||||
|
"Books/servo"
|
||||||
|
"Music"
|
||||||
|
"Videos/gPodder"
|
||||||
|
"Videos/local"
|
||||||
|
"Videos/servo"
|
||||||
|
];
|
||||||
|
|
||||||
|
persist.byStore.plaintext = [
|
||||||
|
# for `watch_later`
|
||||||
|
".local/state/mpv"
|
||||||
|
];
|
||||||
|
fs.".config/mpv/scripts/sane-cast/main.lua".symlink.target = ./sane-cast-main.lua;
|
||||||
|
fs.".config/mpv/scripts/sane-sysvol/main.lua".symlink.target = ./sane-sysvol/main.lua;
|
||||||
|
fs.".config/mpv/scripts/sane-sysvol/non_blocking_popen.lua".symlink.target = ./sane-sysvol/non_blocking_popen.lua;
|
||||||
|
fs.".config/mpv/input.conf".symlink.target = ./input.conf;
|
||||||
|
fs.".config/mpv/mpv.conf".symlink.target = ./mpv.conf;
|
||||||
|
fs.".config/mpv/script-opts/osc.conf".symlink.target = ./osc.conf;
|
||||||
|
fs.".config/mpv/script-opts/console.conf".symlink.target = ./console.conf;
|
||||||
|
fs.".config/mpv/script-opts/uosc.conf".symlink.target = ./uosc.conf;
|
||||||
|
fs.".config/mpv/script-opts/playlistmanager.conf".symlink.target = ./playlistmanager.conf;
|
||||||
|
|
||||||
|
# mime.priority = 200; # default = 100; 200 means to yield to other apps
|
||||||
|
mime.priority = 50; # default = 100; 50 in order to take precedence over vlc.
|
||||||
|
mime.associations."audio/flac" = "mpv.desktop";
|
||||||
|
mime.associations."audio/mpeg" = "mpv.desktop";
|
||||||
|
mime.associations."audio/x-opus+ogg" = "mpv.desktop";
|
||||||
|
mime.associations."audio/x-vorbis+ogg" = "mpv.desktop";
|
||||||
|
mime.associations."video/mp4" = "mpv.desktop";
|
||||||
|
mime.associations."video/quicktime" = "mpv.desktop";
|
||||||
|
mime.associations."video/webm" = "mpv.desktop";
|
||||||
|
mime.associations."video/x-flv" = "mpv.desktop";
|
||||||
|
mime.associations."video/x-matroska" = "mpv.desktop";
|
||||||
|
mime.urlAssociations."^https?://(www.)?youtube.com/watch\?.*v=" = "mpv.desktop";
|
||||||
|
mime.urlAssociations."^https?://(www.)?youtube.com/v/" = "mpv.desktop";
|
||||||
|
mime.urlAssociations."^https?://(www.)?youtu.be/.+" = "mpv.desktop";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
37
hosts/common/programs/mpv/input.conf
Normal file
37
hosts/common/programs/mpv/input.conf
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# docs:
|
||||||
|
# - <https://mpv.io/manual/master/#list-of-input-commands>
|
||||||
|
# - script-binding: <https://mpv.io/manual/master/#command-interface-script-binding>
|
||||||
|
# - properties: <https://mpv.io/manual/master/#property-list>
|
||||||
|
|
||||||
|
# let volume/power keys be interpreted by the system.
|
||||||
|
# this is important for sxmo.
|
||||||
|
# mpv defaults is POWER = close, VOLUME_{UP,DOWN} = adjust application-level volume
|
||||||
|
POWER ignore
|
||||||
|
VOLUME_UP ignore
|
||||||
|
VOLUME_DOWN ignore
|
||||||
|
|
||||||
|
# uosc menu
|
||||||
|
# text after the shebang is parsed by uosc to construct the menu and names
|
||||||
|
menu script-binding uosc/menu
|
||||||
|
s script-binding uosc/subtitles #! Subtitles
|
||||||
|
a script-binding uosc/audio #! Audio tracks
|
||||||
|
q script-binding uosc/stream-quality #! Stream quality
|
||||||
|
p script-binding uosc/items #! Playlist
|
||||||
|
c script-binding uosc/chapters #! Chapters
|
||||||
|
> script-binding uosc/next #! Navigation > Next
|
||||||
|
< script-binding uosc/prev #! Navigation > Prev
|
||||||
|
o script-binding uosc/open-file #! Navigation > Open file
|
||||||
|
# set video-aspect-override "-1" #! Utils > Aspect ratio > Default
|
||||||
|
# set video-aspect-override "16:9" #! Utils > Aspect ratio > 16:9
|
||||||
|
# set video-aspect-override "4:3" #! Utils > Aspect ratio > 4:3
|
||||||
|
# set video-aspect-override "2.35:1" #! Utils > Aspect ratio > 2.35:1
|
||||||
|
# script-binding uosc/audio-device #! Utils > Audio devices
|
||||||
|
# script-binding uosc/editions #! Utils > Editions
|
||||||
|
ctrl+s async screenshot #! Utils > Screenshot
|
||||||
|
alt+i script-binding uosc/keybinds #! Utils > Key bindings
|
||||||
|
O script-binding uosc/show-in-directory #! Utils > Show in directory
|
||||||
|
# script-binding uosc/open-config-directory #! Utils > Open config directory
|
||||||
|
ctrl+r script-binding sane-cast/blast #! Audiocast
|
||||||
|
ctrl+t script-binding sane-cast/go2tv-video #! Cast
|
||||||
|
# script-binding sane-cast/go2tv-stream #! Cast (...) > Stream
|
||||||
|
# script-binding sane-cast/go2tv-gui #! Cast (...) > GUI
|
26
hosts/common/programs/mpv/mpv.conf
Normal file
26
hosts/common/programs/mpv/mpv.conf
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# write ~/.local/state/mpv/watch_later on exit, to allow resume
|
||||||
|
save-position-on-quit=yes
|
||||||
|
# identify resumed files by filename only, since i use so many symlinks and doubt mpv does well with that.
|
||||||
|
ignore-path-in-watch-later-config
|
||||||
|
|
||||||
|
# keep-open: don't exit on completion of last file in playlist
|
||||||
|
keep-open=yes
|
||||||
|
# seeking once at the end of the file causes auto-resume
|
||||||
|
keep-open-pause=no
|
||||||
|
|
||||||
|
# force GUI, even for tracks w/o album art
|
||||||
|
# see: <https://www.reddit.com/r/mpv/comments/rvrrpt/oscosdgui_and_arch_linux/>
|
||||||
|
player-operation-mode=pseudo-gui
|
||||||
|
|
||||||
|
# use uosc instead (for On Screen Controls)
|
||||||
|
osc=no
|
||||||
|
# uosc provides its own seeking/volume indicators, so you also don't need this
|
||||||
|
osd-bar=no
|
||||||
|
# uosc will draw its own window controls if you disable window border
|
||||||
|
border=no
|
||||||
|
|
||||||
|
# ao=alsa so that uosc can work with ao-volume (see my uosc patch)
|
||||||
|
ao=alsa
|
||||||
|
# with `ao-volume`, the max actually is 100.
|
||||||
|
# to go higher you'll have to use the system's native controls.
|
||||||
|
volume-max=100
|
5
hosts/common/programs/mpv/osc.conf
Normal file
5
hosts/common/programs/mpv/osc.conf
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# make the on-screen controls *always* visible
|
||||||
|
# unfortunately, this applies to full-screen as well
|
||||||
|
# - docs: <https://mpv.io/manual/master/#on-screen-controller-visibility>
|
||||||
|
# if uosc is installed, this file is unused
|
||||||
|
visibility=always
|
4
hosts/common/programs/mpv/playlistmanager.conf
Normal file
4
hosts/common/programs/mpv/playlistmanager.conf
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# script docs: <https://github.com/jonniek/mpv-playlistmanager>
|
||||||
|
|
||||||
|
# auto-populate playlist with other files in the same directory, on launch.
|
||||||
|
loadfiles_on_start=yes
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user