Compare commits

..

10 Commits

260 changed files with 8665 additions and 10030 deletions

View File

@ -22,7 +22,7 @@ you might specifically be interested in these files (elaborated further in #key-
- my way of deploying dotfiles/configuring programs per-user:
- [modules/fs/](./modules/fs/default.nix)
- [modules/programs/](./modules/programs/default.nix)
- [modules/users/](./modules/users/default.nix)
- [modules/users.nix](./modules/users.nix)
[nixpkgs]: https://github.com/NixOS/nixpkgs
[sops]: https://github.com/Mic92/sops-nix
@ -109,10 +109,9 @@ i.e. you might find value in using these in your own config:
- `sane.programs.firefox.sandbox.whitelistWayland = true; # allow it to render a wayland window`
- `sane.programs.firefox.sandbox.extraHomePaths = [ "Downloads" ]; # allow it read/write access to ~/Downloads`
- integrated with `fs` and `persist` modules so that programs' config files and persisted data stores are linked into the sandbox w/o any extra involvement.
- `modules/users/`
- `modules/users.nix`
- convenience layer atop the above modules so that you can just write
`fs.".config/git"` instead of `fs."/home/colin/.config/git"`
- per-user services managed by [s6-rc](https://www.skarnet.org/software/s6-rc/)
some things in here could easily find broader use. if you would find benefit in
them being factored out of my config, message me and we could work to make that happen.

25
TODO.md
View File

@ -1,21 +1,9 @@
## BUGS
- moby: megapixels doesn't load in sandbox
- when moby wlan is explicitly set down (via ip link set wlan0 down), /var/lib/trust-dns/dhcp-configs doesn't get reset
- trust-dns: can't recursively resolve api.mangadex.org
- and *sometimes* apple.com fails
- sandbox: `ip netns exec ovpns bash`: doesn't work
- sandbox: link cache means that if i update ~/.config/... files inline, sandboxed programs still see the old version
- mpv: no way to exit fullscreen video on moby
- uosc hides controls on FS, and touch doesn't support unhiding
- Signal restart loop drains battery
- decrease s6 restart time?
- `ssh` access doesn't grant same linux capabilities as login
- ringer (i.e. dino incoming call) doesn't prevent moby from sleeping
- sway mouse/kb hotplug doesn't work
- sysvol (volume overlay): when casting with `blast`, sysvol doesn't react to volume changes
- `nix` operations from lappy hang when `desko` is unreachable
- could at least direct the cache to `http://desko-hn:5001`
## REFACTORING:
- REMOVE DEPRECATED `crypt` from sftpgo_auth_hook
- consolidate ~/dev and ~/ref
- ~/dev becomes a link to ~/ref/cat/mine
- fold hosts/common/home/ssh.nix -> hosts/common/users/colin.nix
@ -32,15 +20,13 @@
### upstreaming
- add updateScripts to all my packages in nixpkgs
- REVIEW/integrate jellyfin dataDir config: <https://github.com/NixOS/nixpkgs/pull/233617>
#### upstreaming to non-nixpkgs repos
- gtk: build schemas even on cross compilation: <https://github.com/NixOS/nixpkgs/pull/247844>
## IMPROVEMENTS:
- systemd/journalctl: use a less shit pager
- there's an env var for it: SYSTEMD_PAGER? and a flag for journalctl
### security/resilience
- validate duplicity backups!
- encrypt more ~ dirs (~/archives, ~/records, ..?)
@ -58,7 +44,7 @@
- <https://github.com/flatpak/xdg-dbus-proxy>
- remove `.ssh` access from Firefox!
- limit access to `~/knowledge/secrets` through an agent that requires GUI approval, so a firefox exploit can't steal all my logins
- port sanebox 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.
- remove /run/wrappers from the sandbox path
- they're mostly useless when using no-new-privs, just an opportunity to forget to specify deps
@ -69,7 +55,6 @@
- integrate `nix check` into Gitea actions?
### user experience
- rofi: sort items case-insensitively
- xdg-desktop-portal shouldn't kill children on exit
- *maybe* a job for `setsid -f`?
- replace starship prompt with something more efficient
@ -94,8 +79,6 @@
- numberlink (generic name for Flow Free). not packaged in Nix
- Neverball (https://neverball.org/screenshots.php). nix: as `neverball`
- blurble (https://linuxphoneapps.org/games/app.drey.blurble/). nix: not as of 2024-02-05
- Trivia Quiz (https://linuxphoneapps.org/games/io.github.nokse22.trivia-quiz/)
- sane-sync-music: remove empty dirs
#### moby
- fix cpuidle (gets better power consumption): <https://xnux.eu/log/077.html>

View File

@ -24,11 +24,11 @@
]
},
"locked": {
"lastModified": 1712014858,
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
"lastModified": 1701473968,
"narHash": "sha256-YcVE5emp1qQ8ieHUnxt1wCZCC3ZfAS+SRRWZ2TMda7E=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
"rev": "34fed993f1674c8d06d58b37ce1e0fe5eebcb9f5",
"type": "github"
},
"original": {
@ -61,11 +61,11 @@
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1715515815,
"narHash": "sha256-yaLScMHNFCH6SbB0HSA/8DWDgK0PyOhCXoFTdHlWkhk=",
"lastModified": 1710184940,
"narHash": "sha256-FzYm4td3FJfzOAuEkCXt3KdUgZuA072OAQXqIq+IAMo=",
"owner": "nix-community",
"repo": "lib-aggregate",
"rev": "09883ca828e8cfaacdb09e29190a7b84ad1d9925",
"rev": "45b75bf534592c0c1c881a1c447f7fdb37a87eaf",
"type": "github"
},
"original": {
@ -99,11 +99,11 @@
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1715804156,
"narHash": "sha256-GtIHP86Cz1kD9xZO/cKbNQACHKdoT9WFbLJAq6W2EDY=",
"lastModified": 1705242886,
"narHash": "sha256-TLj334vRwFtSym3m+NnKcNCnKKPNoTC/TDZL40vmOso=",
"owner": "nix-community",
"repo": "nix-eval-jobs",
"rev": "bb95091f6c6f38f6cfc215a1797a2dd466312c8b",
"rev": "6b03a93296faf174b97546fd573c8b379f523a8d",
"type": "github"
},
"original": {
@ -121,11 +121,11 @@
]
},
"locked": {
"lastModified": 1703863825,
"narHash": "sha256-rXwqjtwiGKJheXB43ybM8NwWB8rO2dSRrEqes0S7F5Y=",
"lastModified": 1701208414,
"narHash": "sha256-xrQ0FyhwTZK6BwKhahIkUVZhMNk21IEI1nUcWSONtpo=",
"owner": "nix-community",
"repo": "nix-github-actions",
"rev": "5163432afc817cf8bd1f031418d1869e4c9d5547",
"rev": "93e39cc1a087d65bcf7a132e75a650c44dd2b734",
"type": "github"
},
"original": {
@ -136,11 +136,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1715037484,
"narHash": "sha256-OUt8xQFmBU96Hmm4T9tOWTu4oCswCzoVl+pxSq/kiFc=",
"lastModified": 1703134684,
"narHash": "sha256-SQmng1EnBFLzS7WSRyPM9HgmZP2kLJcPAz+Ug/nug6o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "ad7efee13e0d216bf29992311536fce1d3eefbef",
"rev": "d6863cbcbbb80e71cecfc03356db1cda38919523",
"type": "github"
},
"original": {
@ -152,11 +152,11 @@
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1715474941,
"narHash": "sha256-CNCqCGOHdxuiVnVkhTpp2WcqSSmSfeQjubhDOcgwGjU=",
"lastModified": 1710031547,
"narHash": "sha256-pkUg3hOKuGWMGF9WEMPPN/G4pqqdbNGJQ54yhyQYDVY=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "58e03b95f65dfdca21979a081aa62db0eed6b1d8",
"rev": "630ebdc047ca96d8126e16bb664c7730dc52f6e6",
"type": "github"
},
"original": {
@ -167,11 +167,11 @@
},
"nixpkgs-next-unpatched": {
"locked": {
"lastModified": 1715839255,
"narHash": "sha256-IKUEASEZKDqOC/q6RP54O3Dz3C2+BBi+VtnIbhBpBbw=",
"lastModified": 1710337169,
"narHash": "sha256-u2/74bhQuWykUZDWUIhHd6IpZiaQ0hSpTBbx0y9opkE=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "1887e39d7e68bb191eb804c0f976ad25b3980595",
"rev": "4ee0840ba2ecc50458ab1677d108afcd691f4815",
"type": "github"
},
"original": {
@ -183,11 +183,11 @@
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1715458492,
"narHash": "sha256-q0OFeZqKQaik2U8wwGDsELEkgoZMK7gvfF6tTXkpsqE=",
"lastModified": 1710033658,
"narHash": "sha256-yiZiVKP5Ya813iYLho2+CcFuuHpaqKc/CoxOlANKcqM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8e47858badee5594292921c2668c11004c3b0142",
"rev": "b17375d3bb7c79ffc52f3538028b2ec06eb79ef8",
"type": "github"
},
"original": {
@ -199,11 +199,11 @@
},
"nixpkgs-unpatched": {
"locked": {
"lastModified": 1715851096,
"narHash": "sha256-ed72tDlrU4/PBWPYoxPk+HFazU3Yny0stTjlGZ7YeMA=",
"lastModified": 1710339354,
"narHash": "sha256-+P5ccUPiLouHexb8aJrUOVOIja9qm+fG57pgAu7uIRs=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "977a49df312d89b7dfbb3579bf13b7dfe23e7878",
"rev": "2dbc8f62d8af7a1ab962e4b20d12b25ddcb86ced",
"type": "github"
},
"original": {
@ -223,11 +223,11 @@
]
},
"locked": {
"lastModified": 1715843614,
"narHash": "sha256-qveerNXc6yF2digoKDR9Hj/o0n8Y3bW/yET6sRochv0=",
"lastModified": 1710317949,
"narHash": "sha256-bwReMiWPA2wYBvKEMhO8pJcu+o+7ocy5hGkSoawTHu0=",
"owner": "nix-community",
"repo": "nixpkgs-wayland",
"rev": "5e2c5345f3204c867c9d4183cbb68069d0f7a951",
"rev": "771cb198c281db6918829651f194bf4db32e342d",
"type": "github"
},
"original": {
@ -254,11 +254,11 @@
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1715482972,
"narHash": "sha256-y1uMzXNlrVOWYj1YNcsGYLm4TOC2aJrwoUY1NjQs9fM=",
"lastModified": 1710195194,
"narHash": "sha256-KFxCJp0T6TJOz1IOKlpRdpsCr9xsvlVuWY/VCiAFnTE=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "b6cb5de2ce57acb10ecdaaf9bbd62a5ff24fa02e",
"rev": "e52d8117b330f690382f1d16d81ae43daeb4b880",
"type": "github"
},
"original": {
@ -291,11 +291,11 @@
]
},
"locked": {
"lastModified": 1711963903,
"narHash": "sha256-N3QDhoaX+paWXHbEXZapqd1r95mdshxToGowtjtYkGI=",
"lastModified": 1702979157,
"narHash": "sha256-RnFBbLbpqtn4AoJGXKevQMCGhra4h6G2MPcuTSZZQ+g=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "49dc4a92b02b8e68798abd99184f228243b6e3ac",
"rev": "2961375283668d867e64129c22af532de8e77734",
"type": "github"
},
"original": {
@ -311,11 +311,11 @@
]
},
"locked": {
"lastModified": 1713198740,
"narHash": "sha256-8SUaqMJdAkMOI9zhvlToL7eCr5Sl+2o2pDQ7nq+HoJU=",
"lastModified": 1707981105,
"narHash": "sha256-YCU1eNslBHabjP+OCY+BxPycEFO9SRUts10MrN9QORE=",
"ref": "refs/heads/master",
"rev": "af8420d1c256d990b5e24de14ad8592a5d85bf77",
"revCount": 239,
"rev": "bb10cd8853d05191e4d62947d93687c462e92c30",
"revCount": 235,
"type": "git",
"url": "https://git.uninsane.org/colin/uninsane"
},

View File

@ -108,22 +108,20 @@
nixpkgs' = patchNixpkgs "master" nixpkgs-unpatched;
nixpkgsCompiledBy = system: nixpkgs'.legacyPackages."${system}";
evalHost = { name, local, target, variant ? null, nixpkgs ? nixpkgs' }: nixpkgs.lib.nixosSystem {
evalHost = { name, local, target, light ? false, nixpkgs ? nixpkgs' }: nixpkgs.lib.nixosSystem {
system = target;
modules = [
{
nixpkgs.buildPlatform.system = local;
# nixpkgs.config.replaceStdenv = { pkgs }: pkgs.ccacheStdenv;
}
(optionalAttrs (local != target) {
# XXX(2023/12/11): cache.nixos.org uses `system = ...` instead of `hostPlatform.system`, and that choice impacts the closure of every package.
# so avoid specifying hostPlatform.system on non-cross builds, so i can use upstream caches.
nixpkgs.hostPlatform.system = target;
})
(optionalAttrs (variant == "light") {
sane.maxBuildCost = 2;
})
(optionalAttrs (variant == "min") {
sane.maxBuildCost = 0;
(optionalAttrs light {
sane.enableSlowPrograms = false;
})
(import ./hosts/instantiate.nix { hostName = name; })
self.nixosModules.default
@ -141,13 +139,11 @@
hosts = {
servo = { name = "servo"; local = "x86_64-linux"; target = "x86_64-linux"; };
desko = { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; };
desko-light = { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; variant = "light"; };
desko-light = { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; light = true; };
lappy = { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; };
lappy-light = { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; variant = "light"; };
lappy-min = { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; variant = "min"; };
lappy-light = { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; light = true; };
moby = { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; };
moby-light = { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; variant = "light"; };
moby-min = { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; variant = "min"; };
moby-light = { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; light = true; };
rescue = { name = "rescue"; local = "x86_64-linux"; target = "x86_64-linux"; };
};
hostsNext = mapAttrs' (h: v: {
@ -289,7 +285,7 @@
# - sandbox friendliness (especially: `git` doesn't have to be run as root)
if [ -n "$addr" ]; then
sudo nix store sign -r -k /run/secrets/nix_signing_key "$storePath"
sudo nix store sign -r -k /run/secrets/nix_serve_privkey "$storePath"
# add more `-v` for more verbosity (up to 5).
# builders-use-substitutes false: optimizes so that the remote machine doesn't try to get paths from its substituters.
# we already have all paths here, and the remote substitution is slow to check and SERIOUSLY flaky on moby in particular.
@ -412,17 +408,14 @@
desko-light = deployApp "desko-light" "desko" "switch";
lappy = deployApp "lappy" "lappy" "switch";
lappy-light = deployApp "lappy-light" "lappy" "switch";
lappy-min = deployApp "lappy-min" "lappy" "switch";
moby = deployApp "moby" "moby" "switch";
moby-light = deployApp "moby-light" "moby" "switch";
moby-min = deployApp "moby-min" "moby" "switch";
moby-test = deployApp "moby" "moby" "test";
servo = deployApp "servo" "servo" "switch";
# like `nixos-rebuild --flake . switch`
self = deployApp "$(hostname)" "" "switch";
self-light = deployApp "$(hostname)-light" "" "switch";
self-min = deployApp "$(hostname)-min" "" "switch";
self = deployApp "$(hostname)" "" "switch";
self-light = deployApp "$(hostname)-light" "" "switch";
type = "app";
program = builtins.toString (pkgs.writeShellScript "deploy-all" ''
@ -438,16 +431,12 @@
desko-light = deployApp "desko-light" "desko" null;
lappy = deployApp "lappy" "lappy" null;
lappy-light = deployApp "lappy-light" "lappy" null;
lappy-min = deployApp "lappy-min" "lappy" null;
moby = deployApp "moby" "moby" null;
moby-light = deployApp "moby-light" "moby" null;
moby-min = deployApp "moby-min" "moby" null;
servo = deployApp "servo" "servo" null;
type = "app";
program = builtins.toString (pkgs.writeShellScript "predeploy-all" ''
# copy the -min/-light variants first; this might be run while waiting on a full build. or the full build failed.
nix run '.#preDeploy.moby-min' -- "$@"
nix run '.#preDeploy.lappy-min' -- "$@"
# copy the -light variants first; this might be run while waiting on a full build. or the full build failed.
nix run '.#preDeploy.moby-light' -- "$@"
nix run '.#preDeploy.lappy-light' -- "$@"
nix run '.#preDeploy.desko-light' -- "$@"
@ -552,9 +541,6 @@
''
# build minimally-usable hosts first, then their full image.
# this gives me a minimal image i can deploy or copy over, early.
${checkHost "lappy-min"}
${checkHost "moby-min"}
${checkHost "desko-light"}
${checkHost "moby-light"}
${checkHost "lappy-light"}

View File

@ -4,16 +4,18 @@
./fs.nix
];
sane.services.trust-dns.asSystemResolver = false; # TEMPORARY: TODO: re-enable trust-dns
# sane.programs.devPkgs.enableFor.user.colin = true;
# sane.guest.enable = true;
# don't enable wifi by default: it messes with connectivity.
# systemd.services.iwd.enable = false;
# systemd.services.wpa_supplicant.enable = false;
# services.distccd.enable = true;
# sane.programs.distcc.enableFor.user.guest = true;
# TODO: remove emulation, but need to fix nixos-rebuild to moby for that.
# sane.roles.build-machine.emulation = true;
sops.secrets.colin-passwd.neededForUsers = true;
sane.ports.openFirewall = true; # for e.g. nix-serve
sane.roles.build-machine.enable = true;
sane.roles.client = true;
sane.roles.dev-machine = true;
@ -21,14 +23,17 @@
sane.services.wg-home.enable = true;
sane.services.wg-home.ip = config.sane.hosts.by-name."desko".wg-home.ip;
sane.services.duplicity.enable = true;
sane.services.nixserve.secretKeyFile = config.sops.secrets.nix_serve_privkey.path;
sane.nixcache.substituters.desko = false;
sane.nixcache.remote-builders.desko = false;
sane.programs.cups.enableFor.user.colin = true;
sane.programs.sway.enableFor.user.colin = true;
sane.programs.iphoneUtils.enableFor.user.colin = true;
sane.programs.steam.enableFor.user.colin = true;
# sane.programs.devPkgs.enableFor.user.colin = true;
sane.programs."gnome.geary".config.autostart = true;
sane.programs.signal-desktop.config.autostart = true;
@ -38,6 +43,10 @@
# needed to use libimobiledevice/ifuse, for iphone sync
services.usbmuxd.enable = true;
# don't enable wifi by default: it messes with connectivity.
systemd.services.iwd.enable = false;
systemd.services.wpa_supplicant.enable = false;
# default config: https://man.archlinux.org/man/snapper-configs.5
# defaults to something like:
# - hourly snapshots

View File

@ -14,12 +14,10 @@
boot.loader.efi.canTouchEfiVariables = false;
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
sane.programs.cups.enableFor.user.colin = true;
sane.programs.stepmania.enableFor.user.colin = true;
sane.programs.sway.enableFor.user.colin = true;
sane.programs."gnome.geary".config.autostart = true;
sane.programs.signal-desktop.config.autostart = true;
sane.programs.stepmania.enableFor.user.colin = true;
sops.secrets.colin-passwd.neededForUsers = true;

View File

@ -1,22 +1,12 @@
# 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, ... }:
{
# we need space in the GPT header to place tow-boot.
# only actually need 1 MB, but better to over-allocate than under-allocate
sane.image.extraGPTPadding = 16 * 1024 * 1024;
sane.image.firstPartGap = 0;
sane.image.installBootloader = ''
dd if=${pkgs.tow-boot-pinephone}/Tow-Boot.noenv.bin of=$out/nixos.img bs=1024 seek=8 conv=notrunc
system.build.img = pkgs.runCommand "nixos_full-disk-image.img" {} ''
cp -v ${config.system.build.img-without-firmware}/nixos.img $out
chmod +w $out
dd if=${pkgs.tow-boot-pinephone}/Tow-Boot.noenv.bin of=$out bs=1024 seek=8 conv=notrunc
'';
}

View File

@ -25,6 +25,10 @@
sane.services.wg-home.enable = true;
sane.services.wg-home.ip = config.sane.hosts.by-name."moby".wg-home.ip;
# for some reason desko -> moby deploys are super flaky when desko is also a nixcache (not true of desko -> lappy deploys, though!)
# > unable to download 'http://desko:5001/<hash>.narinfo': Server returned nothing (no headers, no data) (52)
sane.nixcache.substituters.desko = false;
# XXX colin: phosh doesn't work well with passwordless login,
# so set this more reliable default password should anything go wrong
users.users.colin.initialPassword = "147147";
@ -58,8 +62,18 @@
# HACK/TODO: make `programs.P.env.VAR` behave according to `mime.priority`
sane.programs.firefox.env = lib.mkForce {};
sane.programs.epiphany.env.BROWSER = "epiphany";
sane.programs.pipewire.config = {
# tune so Dino doesn't drop audio
# note the .conf.d approach: using ~/.config/pipewire/pipewire.conf directly breaks all audio,
# presumably because that deletes the defaults entirely whereas the .conf.d approach selectively overrides defaults
sane.user.fs.".config/pipewire/pipewire.conf.d/10-fix-dino-mic-cutout.conf".symlink.text = ''
# config docs: <https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-PipeWire#properties>
# useful to run `pw-top` to see that these settings are actually having effect,
# and `pw-metadata` to see if any settings conflict (e.g. max-quantum < min-quantum)
#
# restart pipewire after editing these files:
# - `systemctl --user restart pipewire`
# - pipewire users will likely stop outputting audio until they are also restarted
#
# there's seemingly two buffers for the mic (see: <https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#pipewire-buffering-explained>)
# 1. Pipewire buffering out of the driver and into its own member.
# 2. Pipewire buffering into Dino.
@ -70,9 +84,11 @@
# `pw-metadata -n settings 0 clock.force-quantum 1024` reduces to about 1 error per second.
# `pw-metadata -n settings 0 clock.force-quantum 2048` reduces to 1 error every < 10s.
# pipewire default config includes `clock.power-of-two-quantum = true`
min-quantum = 2048;
max-quantum = 8192;
};
context.properties = {
default.clock.min-quantum = 2048
default.clock.max-quantum = 8192
}
'';
boot.loader.efi.canTouchEfiVariables = false;
# /boot space is at a premium. default was 20.
@ -112,6 +128,14 @@
# enable rotation sensor
hardware.sensor.iio.enable = true;
# TODO: move elsewhere...
systemd.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
chmod = "${pkgs.coreutils}/bin/chmod";
chown = "${pkgs.coreutils}/bin/chown";

View File

@ -64,5 +64,6 @@
"dialout" # TODO: figure out if dialout is required. that's for /dev/ttyUSB1, but geoclue probably doesn't read that?
];
sane.services.eg25-control.enable = true;
sane.programs.where-am-i.enableFor.user.colin = true;
}

View File

@ -22,12 +22,13 @@
"sane-scripts.stop-all-servo"
];
sane.services.dyn-dns.enable = true;
sane.services.trust-dns.asSystemResolver = false; # TODO: enable once it's all working well
sane.services.wg-home.enable = true;
sane.services.wg-home.visibleToWan = true;
sane.services.wg-home.forwardToWan = true;
sane.services.wg-home.routeThroughServo = false;
sane.services.wg-home.ip = config.sane.hosts.by-name."servo".wg-home.ip;
sane.nixcache.substituters.servo = false;
sane.nixcache.substituters.desko = false;
sane.nixcache.remote-builders.desko = false;
sane.nixcache.remote-builders.servo = false;
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade

View File

@ -8,7 +8,6 @@
# - 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`
# - 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`
# show zfs datasets: `zfs list` (will be empty if haven't imported)
@ -26,7 +25,6 @@
# scrub all zfs pools weekly:
services.zfs.autoScrub.enable = true;
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.
# so, reduce its cache size
# see: <https://askubuntu.com/a/1290387>
@ -35,13 +33,7 @@
# for all tunables, see: `man 4 zfs`
# to update these parameters without rebooting:
# - `echo '4294967296' | sane-sudo-redirect /sys/module/zfs/parameters/zfs_arc_max`
### 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
options zfs zfs_arc_max=4294967296
'';
# 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.
@ -51,7 +43,6 @@
fileSystems."/mnt/pool" = {
device = "pool";
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
sane.programs.sysadminUtils.suggestedPrograms = [ "zfs" ];
@ -91,17 +82,12 @@
};
sane.fs."/mnt/usb-hdd".mount = {};
# FIRST TIME SETUP FOR MEDIA DIRECTORY:
# - set the group stick bit: `sudo find /var/media -type d -exec chmod g+s {} +`
# - 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
# - ensure all files are rwx by group: `setfacl --recursive --modify d:g::rwx /var/media`
# - alternatively, `d:g:media:rwx` to grant `media` group even when file has a different owner, but that's a bit complex
sane.persist.sys.byStore.ext = [{
sane.persist.sys.byStore.plaintext = [{
path = "/var/media";
method = "bind"; #< this HAS to be `bind` if we're going to persist the whole thing but create subdirs, as below.
user = "colin";
group = "media";
mode = "0775";
mode = "0755";
}];
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)
@ -115,7 +101,7 @@
sane.fs."/var/media/Books/Books".dir = {};
sane.fs."/var/media/Books/Visual".dir = {};
sane.fs."/var/media/collections".dir = {};
# sane.fs."/var/media/datasets".dir = {};
sane.fs."/var/media/datasets".dir = {};
sane.fs."/var/media/freeleech".dir = {};
sane.fs."/var/media/Music".dir = {};
sane.fs."/var/media/Pictures".dir = {};
@ -130,6 +116,27 @@
this directory exists on SSD, allowing for speedy access to specific datasets when necessary.
the contents should be a subset of what's in ../media/datasets.
'';
# make sure large media is stored to the HDD
sane.persist.sys.byStore.ext = [
{
user = "colin";
group = "users";
mode = "0777";
path = "/var/media/Videos";
}
{
user = "colin";
group = "users";
mode = "0777";
path = "/var/media/freeleech";
}
{
user = "colin";
group = "users";
mode = "0775";
path = "/var/lib/uninsane/datasets";
}
];
# btrfs doesn't easily support swapfiles
# swapDevices = [

View File

@ -87,7 +87,7 @@ in
}
];
preSetup = ''
${ip} netns add ovpns || (test -e /run/netns/ovpns && echo "ovpns already exists")
${ip} netns add ovpns || echo "ovpns already exists"
'';
postShutdown = ''
${in-ns} ip link del ovpns-veth-b || echo "couldn't delete ovpns-veth-b"

View File

@ -24,7 +24,7 @@ lib.mkIf false
# services.calibre-web.options.calibreLibrary = svc-dir;
services.nginx.virtualHosts."calibre.uninsane.org" = {
forceSSL = true;
addSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://${ip}:${builtins.toString port}";

View File

@ -24,64 +24,50 @@
# that is NOT the case when the STUN server and client A are on the same LAN
# even if client A contacts the STUN server via its WAN address with port reflection enabled.
# hence, there's no obvious way to put the STUN server on the same LAN as either client and expect the rest to work.
# - there an old version which *half worked*, which is:
# - run the turn server in the root namespace.
# - bind the turn server to the veth connecting it to the VPN namespace (so it sends outgoing traffic to the right place).
# - NAT the turn port range from VPN into root namespace (so it receives incomming traffic).
# - this approach would fail the prosody conversations.im check, but i didn't notice *obvious* call routing errors.
#
# debugging:
# - log messages like 'usage: realm=<turn.uninsane.org>, username=<1715915193>, rp=14, rb=1516, sp=8, sb=684'
# - rp = received packets
# - rb = received bytes
# - sp = sent packets
# - sb = sent bytes
{ lib, ... }:
let
# TURN port range (inclusive).
# default coturn behavior is to use the upper quarter of all ports. i.e. 49152 - 65535.
# i believe TURN allocations expire after either 5 or 10 minutes of inactivity.
turnPortLow = 49152; # 49152 = 0xc000
turnPortHigh = turnPortLow + 256;
# TODO: this range could be larger, but right now that's costly because each element is its own UPnP forward
# TURN port range (inclusive)
turnPortLow = 49152;
turnPortHigh = 49167;
turnPortRange = lib.range turnPortLow turnPortHigh;
in
{
# the port definitions are only needed if running in the root net namespace
# sane.ports.ports = lib.mkMerge ([
# {
# "3478" = {
# # this is the "control" port.
# # i.e. no client data is forwarded through it, but it's where clients request tunnels.
# protocol = [ "tcp" "udp" ];
# # visibleTo.lan = true;
# # visibleTo.wan = true;
# visibleTo.ovpn = true; # forward traffic from the VPN to the root NS
# description = "colin-stun-turn";
# };
# "5349" = {
# # the other port 3478 also supports TLS/DTLS, but presumably clients wanting TLS will default 5349
# protocol = [ "tcp" ];
# # visibleTo.lan = true;
# # visibleTo.wan = true;
# visibleTo.ovpn = true;
# description = "colin-stun-turn-over-tls";
# };
# }
# ] ++ (builtins.map
# (port: {
# "${builtins.toString port}" = let
# count = port - turnPortLow + 1;
# numPorts = turnPortHigh - turnPortLow + 1;
# in {
# protocol = [ "tcp" "udp" ];
# # visibleTo.lan = true;
# # visibleTo.wan = true;
# visibleTo.ovpn = true;
# description = "colin-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
# };
# })
# turnPortRange
# ));
sane.ports.ports = lib.mkMerge ([
{
"3478" = {
# this is the "control" port.
# i.e. no client data is forwarded through it, but it's where clients request tunnels.
protocol = [ "tcp" "udp" ];
# visibleTo.lan = true;
# visibleTo.wan = true;
visibleTo.ovpn = true;
description = "colin-stun-turn";
};
"5349" = {
# the other port 3478 also supports TLS/DTLS, but presumably clients wanting TLS will default 5349
protocol = [ "tcp" ];
# visibleTo.lan = true;
# visibleTo.wan = true;
visibleTo.ovpn = true;
description = "colin-stun-turn-over-tls";
};
}
] ++ (builtins.map
(port: {
"${builtins.toString port}" = let
count = port - turnPortLow + 1;
numPorts = turnPortHigh - turnPortLow + 1;
in {
protocol = [ "tcp" "udp" ];
# visibleTo.lan = true;
# visibleTo.wan = true;
visibleTo.ovpn = true;
description = "colin-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
};
})
turnPortRange
));
services.nginx.virtualHosts."turn.uninsane.org" = {
# allow ACME to procure a cert via nginx for this domain
@ -117,28 +103,22 @@ in
services.coturn.realm = "turn.uninsane.org";
services.coturn.cert = "/var/lib/acme/turn.uninsane.org/fullchain.pem";
services.coturn.pkey = "/var/lib/acme/turn.uninsane.org/key.pem";
#v disable to allow unauthenticated access (or set `services.coturn.no-auth = true`)
services.coturn.use-auth-secret = true;
services.coturn.static-auth-secret-file = "/var/lib/coturn/shared_secret.bin";
services.coturn.lt-cred-mech = true; #< XXX: use-auth-secret overrides lt-cred-mech
services.coturn.lt-cred-mech = true;
services.coturn.min-port = turnPortLow;
services.coturn.max-port = turnPortHigh;
# services.coturn.secure-stun = true;
services.coturn.extraConfig = lib.concatStringsSep "\n" [
"verbose"
# "Verbose" #< even MORE verbosity than "verbose" (it's TOO MUCH verbosity really)
"no-multicast-peers" # disables sending to IPv4 broadcast addresses (e.g. 224.0.0.0/3)
# "listening-ip=10.0.1.5" "external-ip=185.157.162.178" #< 2024/04/25: works, if running in root namespace
"listening-ip=185.157.162.178" "external-ip=185.157.162.178"
# old attempts:
# "Verbose" #< even MORE verbosity than "verbose"
# "no-multicast-peers" # disables sending to IPv4 broadcast addresses (e.g. 224.0.0.0/3)
"listening-ip=10.0.1.5"
# "external-ip=185.157.162.178/10.0.1.5"
"external-ip=185.157.162.178"
# "listening-ip=10.78.79.51" # can be specified multiple times; omit for *
# "external-ip=97.113.128.229/10.78.79.51"
# "external-ip=97.113.128.229"
# "mobility" # "mobility with ICE (MICE) specs support" (?)
];
systemd.services.coturn.serviceConfig.NetworkNamespacePath = "/run/netns/ovpns";
}

View File

@ -1,22 +1,6 @@
#!/usr/bin/env nix-shell
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ps.pyln-client ])"
"""
clightning-sane: helper to perform common Lightning node admin operations:
- view channel balances
- rebalance channels
COMMON OPERATIONS:
- view channel balances: `clightning-sane status`
- rebalance channels to improve routability (without paying any fees): `clightning-sane autobalance`
FULL OPERATION:
- `clightning-sane status --full`
- `P$`: represents how many msats i've captured in fees from this channel.
- `COST`: rough measure of how much it's "costing" me to let my channel partner hold funds on his side of the channel.
this is based on the notion that i only capture fees from outbound transactions, and so the channel partner holding all liquidity means i can't capture fees on that liquidity.
"""
# pyln-client docs: <https://github.com/ElementsProject/lightning/tree/master/contrib/pyln-client>
# terminology:
# - "scid": "Short Channel ID", e.g. 123456x7890x0
@ -742,7 +726,7 @@ def main():
logging.basicConfig()
logger.setLevel(logging.INFO)
parser = argparse.ArgumentParser(description=__doc__)
parser = argparse.ArgumentParser(description="rebalance lightning channel balances")
parser.add_argument("--verbose", action="store_true", help="more logging")
parser.add_argument("--min-msat", default="999", help="min transaction size")
parser.add_argument("--max-msat", default="1000000", help="max transaction size")

View File

@ -20,6 +20,7 @@
./navidrome.nix
./nginx.nix
./nixos-prebuild.nix
./nixserve.nix
./ntfy
./pict-rs.nix
./pleroma.nix

View File

@ -2,7 +2,7 @@
{
imports = [
./nfs.nix
./sftpgo
./sftpgo.nix
];
users.groups.export = {};

View File

@ -26,7 +26,7 @@
description = "NFS server portmapper";
};
sane.ports.ports."2049" = {
protocol = [ "tcp" "udp" ];
protocol = [ "tcp" ];
visibleTo.lan = true;
description = "NFS server";
};
@ -51,23 +51,6 @@
services.nfs.server.mountdPort = 4002;
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:
# fspoint visibility(options)
# options:
@ -102,20 +85,13 @@
in "${export} 10.78.79.0/22(${lib.concatStringsSep "," lanOpts}) 10.0.10.0/24(${lib.concatStringsSep "," vpnOpts})";
in lib.concatStringsSep "\n" [
(fmtExport {
export = "/";
export = "/var/export";
baseOpts = [ "crossmnt" "fsid=root" ];
extraLanOpts = [ "ro" ];
extraVpnOpts = [ "rw" "no_root_squash" ];
})
(fmtExport {
# 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";
export = "/var/export/playground";
baseOpts = [
"mountpoint"
"all_squash"

View File

@ -0,0 +1,186 @@
# docs:
# - <https://github.com/drakkan/sftpgo>
# - config options: <https://github.com/drakkan/sftpgo/blob/main/docs/full-configuration.md>
# - config defaults: <https://github.com/drakkan/sftpgo/blob/main/sftpgo.json>
# - nixos options: <repo:nixos/nixpkgs:nixos/modules/services/web-apps/sftpgo.nix>
# - nixos example: <repo:nixos/nixpkgs:nixos/tests/sftpgo.nix>
#
# sftpgo is a FTP server that also supports WebDAV, SFTP, and web clients.
#
# TODO: change umask so sftpgo-created files default to 644.
# - it does indeed appear that the 600 is not something sftpgo is explicitly doing.
{ config, lib, pkgs, sane-lib, ... }:
let
# user permissions:
# - see <repo:drakkan/sftpgo:internal/dataprovider/user.go>
# - "*" = grant all permissions
# - read-only perms:
# - "list" = list files and directories
# - "download"
# - rw perms:
# - "upload"
# - "overwrite" = allow uploads to replace existing files
# - "delete" = delete files and directories
# - "delete_files"
# - "delete_dirs"
# - "rename" = rename files and directories
# - "rename_files"
# - "rename_dirs"
# - "create_dirs"
# - "create_symlinks"
# - "chmod"
# - "chown"
# - "chtimes" = change atime/mtime (access and modification times)
#
# home_dir:
# - it seems (empirically) that a user can't cd above their home directory.
# though i don't have a reference for that in the docs.
authResponseSuccess = {
status = 1;
username = "anonymous";
expiration_date = 0;
home_dir = "/var/export";
# uid/gid 0 means to inherit sftpgo uid.
# - i.e. users can't read files which Linux user `sftpgo` can't read
# - uploaded files belong to Linux user `sftpgo`
# other uid/gid values aren't possible for localfs backend, unless i let sftpgo use `sudo`.
uid = 0;
gid = 0;
# uid = 65534;
# gid = 65534;
max_sessions = 0;
# quota_*: 0 means to not use SFTP's quota system
quota_size = 0;
quota_files = 0;
permissions = {
"/" = [ "list" "download" ];
"/playground" = [
# read-only:
"list"
"download"
# write:
"upload"
"overwrite"
"delete"
"rename"
"create_dirs"
"create_symlinks"
# intentionally omitted:
# "chmod"
# "chown"
# "chtimes"
];
};
upload_bandwidth = 0;
download_bandwidth = 0;
filters = {
allowed_ip = [];
denied_ip = [];
};
public_keys = [];
# other fields:
# ? groups
# ? virtual_folders
};
authResponseFail = {
username = "";
};
authSuccessJson = pkgs.writeText "sftp-auth-success.json" (builtins.toJSON authResponseSuccess);
authFailJson = pkgs.writeText "sftp-auth-fail.json" (builtins.toJSON authResponseFail);
unwrappedAuthProgram = pkgs.static-nix-shell.mkBash {
pname = "sftpgo_external_auth_hook";
srcRoot = ./.;
pkgs = [ "coreutils" ];
};
authProgram = pkgs.writeShellScript "sftpgo-auth-hook" ''
${unwrappedAuthProgram}/bin/sftpgo_external_auth_hook ${authFailJson} ${authSuccessJson}
'';
in
{
# Client initiates a FTP "control connection" on port 21.
# - this handles the client -> server commands, and the server -> client status, but not the actual data
# - file data, directory listings, etc need to be transferred on an ephemeral "data port".
# - 50000-50100 is a common port range for this.
sane.ports.ports = {
"21" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
description = "colin-FTP server";
};
} // (sane-lib.mapToAttrs
(port: {
name = builtins.toString port;
value = {
protocol = [ "tcp" ];
visibleTo.lan = true;
description = "colin-FTP server data port range";
};
})
(lib.range 50000 50100)
);
services.sftpgo = {
enable = true;
group = "export";
settings = {
ftpd = {
bindings = [
{
# binding this means any wireguard client can connect
address = "10.0.10.5";
port = 21;
debug = true;
}
{
# binding this means any LAN client can connect
address = "10.78.79.51";
port = 21;
debug = true;
}
];
# active mode is susceptible to "bounce attacks", without much benefit over passive mode
disable_active_mode = true;
hash_support = true;
passive_port_range = {
start = 50000;
end = 50100;
};
banner = ''
Welcome, friends, to Colin's read-only FTP server! Also available via NFS on the same host.
Username: "anonymous"
Password: "anonymous"
CONFIGURE YOUR CLIENT FOR "PASSIVE" mode, e.g. `ftp --passive uninsane.org`
Please let me know if anything's broken or not as it should be. Otherwise, browse and DL freely :)
'';
};
data_provider = {
driver = "memory";
external_auth_hook = "${authProgram}";
# track_quota:
# - 0: disable quota tracking
# - 1: quota is updated on every upload/delete, even if user has no quota restriction
# - 2: quota is updated on every upload/delete, but only if user/folder has a quota restriction (default, i think)
# track_quota = 2;
};
};
};
users.users.sftpgo.extraGroups = [ "export" ];
systemd.services.sftpgo = {
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
serviceConfig = {
ReadOnlyPaths = [ "/var/export" ];
ReadWritePaths = [ "/var/export/playground" ];
Restart = "always";
RestartSec = "20s";
};
};
}

View File

@ -1,167 +0,0 @@
# docs:
# - <https://github.com/drakkan/sftpgo>
# - config options: <https://github.com/drakkan/sftpgo/blob/main/docs/full-configuration.md>
# - config defaults: <https://github.com/drakkan/sftpgo/blob/main/sftpgo.json>
# - nixos options: <repo:nixos/nixpkgs:nixos/modules/services/web-apps/sftpgo.nix>
# - nixos example: <repo:nixos/nixpkgs:nixos/tests/sftpgo.nix>
#
# sftpgo is a FTP server that also supports WebDAV, SFTP, and web clients.
{ config, lib, pkgs, sane-lib, ... }:
let
external_auth_hook = pkgs.static-nix-shell.mkPython3Bin {
pname = "external_auth_hook";
srcRoot = ./.;
};
# Client initiates a FTP "control connection" on port 21.
# - this handles the client -> server commands, and the server -> client status, but not the actual data
# - file data, directory listings, etc need to be transferred on an ephemeral "data port".
# - 50000-50100 is a common port range for this.
# 50000 is used by soulseek.
passiveStart = 50050;
passiveEnd = 50070;
in
{
sane.ports.ports = {
"21" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
# visibleTo.wan = true;
description = "colin-FTP server";
};
"990" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-FTPS server";
};
} // (sane-lib.mapToAttrs
(port: {
name = builtins.toString port;
value = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-FTP server data port range";
};
})
(lib.range passiveStart passiveEnd)
);
# use nginx/acme to produce a cert for FTPS
services.nginx.virtualHosts."ftp.uninsane.org" = {
addSSL = true;
enableACME = true;
};
sane.dns.zones."uninsane.org".inet.CNAME."ftp" = "native";
services.sftpgo = {
enable = true;
group = "export";
package = lib.warnIf (lib.versionOlder "2.5.6" pkgs.sftpgo.version) "sftpgo update: safe to use nixpkgs' sftpgo but keep my own `patches`" pkgs.buildGoModule {
inherit (pkgs.sftpgo) name ldflags nativeBuildInputs doCheck subPackages postInstall passthru meta;
version = "2.5.6-unstable-2024-04-18";
src = pkgs.fetchFromGitHub {
# need to use > 2.5.6 for sftpgo_safe_fileinfo.patch to apply
owner = "drakkan";
repo = "sftpgo";
rev = "950cf67e4c03a12c7e439802cabbb0b42d4ee5f5";
hash = "sha256-UfiFd9NK3DdZ1J+FPGZrM7r2mo9xlKi0dsSlLEinYXM=";
};
vendorHash = "sha256-n1/9A2em3BCtFX+132ualh4NQwkwewMxYIMOphJEamg=";
patches = (pkgs.sftpgo.patches or []) ++ [
# fix for compatibility with kodi:
# ftp LIST operation returns entries over-the-wire like:
# - dgrwxrwxr-x 1 ftp ftp 9 Apr 9 15:05 Videos
# however not all clients understand all mode bits (like that `g`, indicating SGID / group sticky bit).
# instead, only send mode bits which are well-understood.
# the full set of bits, from which i filter, is found here: <https://pkg.go.dev/io/fs#FileMode>
./safe_fileinfo.patch
];
};
settings = {
ftpd = {
bindings = [
{
# binding this means any wireguard client can connect
address = "10.0.10.5";
port = 21;
debug = true;
}
{
# binding this means any LAN client can connect (also WAN traffic forwarded from the gateway)
address = "10.78.79.51";
port = 21;
debug = true;
}
{
# binding this means any wireguard client can connect
address = "10.0.10.5";
port = 990;
debug = true;
tls_mode = 2; # 2 = "implicit FTPS": client negotiates TLS before any FTP command.
}
{
# binding this means any LAN client can connect (also WAN traffic forwarded from the gateway)
address = "10.78.79.51";
port = 990;
debug = true;
tls_mode = 2; # 2 = "implicit FTPS": client negotiates TLS before any FTP command.
}
];
# active mode is susceptible to "bounce attacks", without much benefit over passive mode
disable_active_mode = true;
hash_support = true;
passive_port_range = {
start = passiveStart;
end = passiveEnd;
};
certificate_file = "/var/lib/acme/ftp.uninsane.org/full.pem";
certificate_key_file = "/var/lib/acme/ftp.uninsane.org/key.pem";
banner = ''
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"
Password: "anonymous"
CONFIGURE YOUR CLIENT FOR "PASSIVE" MODE, e.g. `ftp --passive ftp.uninsane.org`.
Please let me know if anything's broken or not as it should be. Otherwise, browse and transfer freely :)
'';
};
data_provider = {
driver = "memory";
external_auth_hook = "${external_auth_hook}/bin/external_auth_hook";
# track_quota:
# - 0: disable quota tracking
# - 1: quota is updated on every upload/delete, even if user has no quota restriction
# - 2: quota is updated on every upload/delete, but only if user/folder has a quota restriction (default, i think)
# track_quota = 2;
};
};
};
users.users.sftpgo.extraGroups = [
"export"
"media"
"nginx" # to access certs
];
systemd.services.sftpgo = {
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
serviceConfig = {
ReadWritePaths = [ "/var/export" ];
Restart = "always";
RestartSec = "20s";
UMask = lib.mkForce "0002";
};
};
}

View File

@ -1,157 +0,0 @@
#!/usr/bin/env nix-shell
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])"
# vim: set filetype=python :
#
# available environment variables:
# - SFTPGO_AUTHD_USERNAME
# - SFTPGO_AUTHD_USER
# - SFTPGO_AUTHD_IP
# - SFTPGO_AUTHD_PROTOCOL = { "DAV", "FTP", "HTTP", "SSH" }
# - SFTPGO_AUTHD_PASSWORD
# - SFTPGO_AUTHD_PUBLIC_KEY
# - SFTPGO_AUTHD_KEYBOARD_INTERACTIVE
# - SFTPGO_AUTHD_TLS_CERT
#
# user permissions:
# - see <repo:drakkan/sftpgo:internal/dataprovider/user.go>
# - "*" = grant all permissions
# - read-only perms:
# - "list" = list files and directories
# - "download"
# - rw perms:
# - "upload"
# - "overwrite" = allow uploads to replace existing files
# - "delete" = delete files and directories
# - "delete_files"
# - "delete_dirs"
# - "rename" = rename files and directories
# - "rename_files"
# - "rename_dirs"
# - "create_dirs"
# - "create_symlinks"
# - "chmod"
# - "chown"
# - "chtimes" = change atime/mtime (access and modification times)
#
# home_dir:
# - it seems (empirically) that a user can't cd above their home directory.
# though i don't have a reference for that in the docs.
import crypt
import json
import os
from hmac import compare_digest
authFail = dict(username="")
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()

View File

@ -1,32 +0,0 @@
diff --git a/internal/ftpd/handler.go b/internal/ftpd/handler.go
index 036c3977..33211261 100644
--- a/internal/ftpd/handler.go
+++ b/internal/ftpd/handler.go
@@ -169,7 +169,7 @@ func (c *Connection) Stat(name string) (os.FileInfo, error) {
}
return nil, err
}
- return fi, nil
+ return vfs.NewFileInfo(name, fi.IsDir(), fi.Size(), fi.ModTime(), false), nil
}
// Name returns the name of this connection
@@ -315,7 +315,17 @@ func (c *Connection) ReadDir(name string) (ftpserver.DirLister, error) {
}, nil
}
- return c.ListDir(name)
+ lister, err := c.ListDir(name)
+ if err != nil {
+ return nil, err
+ }
+ return &patternDirLister{
+ DirLister: lister,
+ pattern: "*",
+ lastCommand: c.clientContext.GetLastCommand(),
+ dirName: name,
+ connectionPath: c.clientContext.Path(),
+ }, nil
}
// GetHandle implements ClientDriverExtentionFileTransfer

View File

@ -0,0 +1,23 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p coreutils
# vim: set filetype=bash :
#
# available environment variables:
# - SFTPGO_AUTHD_USERNAME
# - SFTPGO_AUTHD_USER
# - SFTPGO_AUTHD_IP
# - SFTPGO_AUTHD_PROTOCOL = { "DAV", "FTP", "HTTP", "SSH" }
# - SFTPGO_AUTHD_PASSWORD
# - SFTPGO_AUTHD_PUBLIC_KEY
# - SFTPGO_AUTHD_KEYBOARD_INTERACTIVE
# - SFTPGO_AUTHD_TLS_CERT
#
#
# call with <script_name> /path/to/fail/response.json /path/to/success/response.json
if [ "$SFTPGO_AUTHD_USERNAME" = "anonymous" ]; then
cat "$2"
else
cat "$1"
fi

View File

@ -20,7 +20,7 @@
--ignore-panel=HOSTS \
--ws-url=wss://sink.uninsane.org:443/ws \
--port=7890 \
-o /var/lib/goaccess/index.html
-o /var/lib/uninsane/sink/index.html
'';
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
Type = "simple";
@ -28,19 +28,17 @@
RestartSec = "10s";
# hardening
# TODO: run as `goaccess` user and add `goaccess` user to group `nginx`.
WorkingDirectory = "/tmp";
NoNewPrivileges = true;
PrivateDevices = "yes";
PrivateTmp = true;
ProtectHome = "read-only";
ProtectSystem = "strict";
SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @memlock @module @mount @obsolete @privileged @reboot @resources @setuid @swap @raw-io";
ReadOnlyPaths = "/";
ReadWritePaths = [ "/proc/self" "/var/lib/uninsane/sink" ];
PrivateDevices = "yes";
ProtectKernelModules = "yes";
ProtectKernelTunables = "yes";
ProtectSystem = "strict";
ReadOnlyPaths = [ "/var/log/nginx" ];
ReadWritePaths = [ "/proc/self" "/var/lib/goaccess" ];
StateDirectory = "goaccess";
SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @memlock @module @mount @obsolete @privileged @reboot @resources @setuid @swap @raw-io";
WorkingDirectory = "/var/lib/goaccess";
};
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
@ -51,7 +49,7 @@
addSSL = true;
enableACME = true;
# inherit kTLS;
root = "/var/lib/goaccess";
root = "/var/lib/uninsane/sink";
locations."/ws" = {
proxyPass = "http://127.0.0.1:7890";

View File

@ -1,4 +1,4 @@
{ lib, pkgs, ... }:
{ ... }:
{
sane.persist.sys.byStore.plaintext = [
@ -12,8 +12,6 @@
systemd.services.jackett.serviceConfig = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";
ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect 185.157.162.178" ]; # abort if public IP is not as expected
# patch jackett to listen on the public interfaces
# ExecStart = lib.mkForce "${pkgs.jackett}/bin/Jackett --NoUpdates --DataFolder /var/lib/jackett/.config/Jackett --ListenPublic";
};

View File

@ -75,7 +75,7 @@
# Jellyfin multimedia server
# this is mostly taken from the official jellfin.org docs
services.nginx.virtualHosts."jelly.uninsane.org" = {
forceSSL = true;
addSSL = true;
enableACME = true;
# inherit kTLS;

View File

@ -12,7 +12,7 @@ in
services.komga.port = 11319; # chosen at random
services.nginx.virtualHosts."komga.uninsane.org" = {
forceSSL = true;
addSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://127.0.0.1:${builtins.toString port}";

View File

@ -10,21 +10,16 @@ let
uiPort = 1234; # default ui port is 1234
backendPort = 8536; # default backend port is 8536
#^ i guess the "backend" port is used for federation?
pict-rs = pkgs.pict-rs;
# pict-rs = pkgs.pict-rs.overrideAttrs (upstream: {
# # as of v0.4.2, all non-GIF video is forcibly transcoded.
# # that breaks lemmy, because of the request latency.
# # and it eats up hella CPU.
# # pict-rs is iffy around video altogether: mp4 seems the best supported.
# # XXX: this patch no longer applies after 0.5.10 -> 0.5.11 update.
# # 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 {'
# '';
# });
pict-rs = pkgs.pict-rs.overrideAttrs (upstream: {
# as of v 0.4.2, all non-GIF video is forcibly transcoded.
# that breaks lemmy, because of the request latency.
# and it eats up hella CPU.
# pict-rs is iffy around video altogether: mp4 seems the best supported.
postPatch = (upstream.postPatch or "") + ''
substituteInPlace src/validate.rs \
--replace 'if transcode_options.needs_reencode() {' 'if false {'
'';
});
in {
services.lemmy = {
enable = true;

View File

@ -89,16 +89,6 @@ in
disable_symlinks on;
'';
};
locations."/share/Milkbags/" = {
alias = "/var/media/Videos/Milkbags/";
extraConfig = ''
# autoindex => render directory listings
autoindex on;
# don't follow any symlinks when serving files
# otherwise it allows a directory escape
disable_symlinks on;
'';
};
# allow matrix users to discover that @user:uninsane.org is reachable via matrix.uninsane.org
locations."= /.well-known/matrix/server".extraConfig =

View File

@ -0,0 +1,21 @@
{ config, ... }:
{
services.nginx.virtualHosts."nixcache.uninsane.org" = {
addSSL = true;
enableACME = true;
# inherit kTLS;
# serverAliases = [ "nixcache" ];
locations."/".extraConfig = ''
proxy_pass http://localhost:${toString config.services.nix-serve.port};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
'';
};
sane.dns.zones."uninsane.org".inet.CNAME."nixcache" = "native";
sane.services.nixserve.enable = true;
sane.services.nixserve.secretKeyFile = config.sops.secrets.nix_serve_privkey.path;
}

View File

@ -25,7 +25,7 @@ in
config :pleroma, Pleroma.Web.Endpoint,
url: [host: "fed.uninsane.org", scheme: "https", port: 443],
http: [ip: {127, 0, 0, 1}, port: 4040]
http: [ip: {127, 0, 0, 1}, port: 4000]
# secret_key_base: "{secrets.pleroma.secret_key_base}",
# signing_salt: "{secrets.pleroma.signing_salt}"
@ -167,7 +167,7 @@ in
enableACME = true;
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:4040";
proxyPass = "http://127.0.0.1:4000";
recommendedProxySettings = true;
# documented: https://git.pleroma.social/pleroma/pleroma/-/blob/develop/installation/pleroma.nginx
extraConfig = ''

View File

@ -3,15 +3,10 @@
#
# config precedence (higher precedence overrules lower precedence):
# - 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, pkgs, ... }:
{ config, lib, ... }:
{
sane.persist.sys.byStore.plaintext = [
{ user = "slskd"; group = "media"; path = "/var/lib/slskd"; method = "bind"; }
{ user = "slskd"; group = "slskd"; path = "/var/lib/slskd"; method = "bind"; }
];
sops.secrets."slskd_env" = {
owner = config.users.users.slskd.name;
@ -20,7 +15,7 @@
users.users.slskd.extraGroups = [ "media" ];
sane.ports.ports."50300" = {
sane.ports.ports."50000" = {
protocol = [ "tcp" ];
# not visible to WAN: i run this in a separate netns
visibleTo.ovpn = true;
@ -33,14 +28,12 @@
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://10.0.1.6:5030";
proxyPass = "http://10.0.1.6:5001";
proxyWebsockets = 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)
services.slskd.environmentFile = config.sops.secrets.slskd_env.path;
services.slskd.settings = {
@ -69,12 +62,13 @@
# flags.volatile = true; # store searches and active transfers in RAM (completed transfers still go to disk). rec for btrfs/zfs
};
systemd.services.slskd.serviceConfig = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";
ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect 185.157.162.178" ]; # abort if public IP is not as expected
Restart = lib.mkForce "always"; # exits "success" when it fails to connect to soulseek server
RestartSec = "60s";
systemd.services.slskd = {
serviceConfig = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";
Restart = lib.mkForce "always"; # exits "success" when it fails to connect to soulseek server
RestartSec = "60s";
Group = "media";
};
};
}

View File

@ -22,65 +22,6 @@ let
--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; [
acl
coreutils
findutils
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/"
# make the media rwx by anyone in the group
destructive find "$MEDIA_DIR" -type d -exec setfacl --recursive --modify d:g::rwx,o::rx {} \;
destructive find "$MEDIA_DIR" -type d -exec chmod g+rw,a+rx {} \;
# if there's a single directory inside the media dir, then inline that
subdirs=("$MEDIA_DIR"/*)
if [ ''${#subdirs} -eq 1 ]; then
dirname="''${subdirs[0]}"
if [ -d "$dirname" ]; then
mv "$dirname"/* "$MEDIA_DIR/" && rmdir "$dirname"
fi
fi
# remove noisy files:
find "$MEDIA_DIR/" -type f \(\
-iname 'www.YTS.*.jpg' \
-o -iname 'WWW.YIFY*.COM.jpg' \
-o -iname 'YIFY*.com.txt' \
-o -iname 'YTS*.com.txt' \
\) -exec rm {} \;
# 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
{
sane.persist.sys.byStore.plaintext = [
@ -105,8 +46,8 @@ in
# DOCUMENTATION/options list: <https://github.com/transmission/transmission/blob/main/docs/Editing-Configuration-Files.md#options>
# message-level = 3; #< enable for debug logging. 0-3, default is 2.
# 10.0.1.6 => allow rpc only from the root servo ns. it'll tunnel things to the net, if need be.
rpc-bind-address = "10.0.1.6";
# 0.0.0.0 => allow rpc from any host: we gate it via firewall and auth requirement
rpc-bind-address = "0.0.0.0";
#rpc-host-whitelist = "bt.uninsane.org";
#rpc-whitelist = "*.*.*.*";
rpc-authentication-required = true;
@ -116,10 +57,6 @@ in
rpc-password = "{503fc8928344f495efb8e1f955111ca5c862ce0656SzQnQ5";
rpc-whitelist-enabled = false;
# force behind ovpns in case the NetworkNamespace fails somehow
bind-address-ipv4 = "185.157.162.178";
port-forwarding-enabled = false;
# hopefully, make the downloads world-readable
# umask = 0; #< default is 2: i.e. deny writes from world
@ -135,23 +72,11 @@ in
# see: https://git.zknt.org/mirror/transmission/commit/cfce6e2e3a9b9d31a9dafedd0bdc8bf2cdb6e876?lang=bg-BG
anti-brute-force-enabled = false;
inherit download-dir;
incomplete-dir = "${download-dir}/incomplete";
download-dir = "/var/media";
incomplete-dir = "/var/media/incomplete";
# transmission regularly fails to move stuff from the incomplete dir to the main one, so disable:
# TODO: uncomment this line!
incomplete-dir-enabled = false;
# 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" ];
@ -159,11 +84,8 @@ in
systemd.services.transmission.serviceConfig = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";
ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect 185.157.162.178" ]; # abort if public IP is not as expected
Restart = "on-failure";
RestartSec = "30s";
BindPaths = [ "/var/media" ]; #< so it can move completed torrents into the media library
};
# service to automatically backup torrents i add to transmission

View File

@ -2,11 +2,19 @@
{ config, lib, pkgs, ... }:
let
dyn-dns = config.sane.services.dyn-dns;
nativeAddrs = lib.mapAttrs (_name: builtins.head) config.sane.dns.zones."uninsane.org".inet.A;
bindOvpn = "10.0.1.5";
in
in lib.mkMerge [
{
services.trust-dns.enable = true;
# don't bind to IPv6 until i explicitly test that stack
services.trust-dns.settings.listen_addrs_ipv6 = [];
services.trust-dns.quiet = true;
# FIXME(2023/11/26): services.trust-dns.debug doesn't log requests: use RUST_LOG=debug env for that.
# - see: <https://github.com/hickory-dns/hickory-dns/issues/2082>
# services.trust-dns.debug = true;
sane.ports.ports."53" = {
protocol = [ "udp" "tcp" ];
visibleTo.lan = true;
@ -58,6 +66,23 @@ in
services.trust-dns.settings.zones = [ "uninsane.org" ];
# TODO: can i transform this into some sort of service group?
# have `systemctl restart trust-dns.service` restart all the individual services?
systemd.services.trust-dns.serviceConfig = {
DynamicUser = lib.mkForce false;
User = "trust-dns";
Group = "trust-dns";
wantedBy = lib.mkForce [];
};
systemd.services.trust-dns.enable = false;
users.groups.trust-dns = {};
users.users.trust-dns = {
group = "trust-dns";
isSystemUser = true;
};
# sane.services.dyn-dns.restartOnChange = [ "trust-dns.service" ];
networking.nat.enable = true;
networking.nat.extraCommands = ''
@ -82,73 +107,98 @@ in
visibleTo.lan = true;
description = "colin-redirected-dns-for-lan-namespace";
};
}
{
systemd.services =
let
sed = "${pkgs.gnused}/bin/sed";
stateDir = "/var/lib/trust-dns";
zoneTemplate = pkgs.writeText "uninsane.org.zone.in" config.sane.dns.zones."uninsane.org".rendered;
zoneDirFor = flavor: "${stateDir}/${flavor}";
zoneFor = flavor: "${zoneDirFor flavor}/uninsane.org.zone";
mkTrustDnsService = opts: flavor: let
flags = let baseCfg = config.services.trust-dns; in
(lib.optional baseCfg.debug "--debug") ++ (lib.optional baseCfg.quiet "--quiet");
flagsStr = builtins.concatStringsSep " " flags;
sane.services.trust-dns.enable = true;
sane.services.trust-dns.instances = let
mkSubstitutions = flavor: {
"%AWAN%" = "$(cat '${dyn-dns.ipPath}')";
"%CNAMENATIVE%" = "servo.${flavor}";
"%ANATIVE%" = nativeAddrs."servo.${flavor}";
"%AOVPNS%" = "185.157.162.178";
anative = nativeAddrs."servo.${flavor}";
toml = pkgs.formats.toml { };
configTemplate = opts.config or (toml.generate "trust-dns-${flavor}.toml" (
(
lib.filterAttrsRecursive (_: v: v != null) config.services.trust-dns.settings
) // {
listen_addrs_ipv4 = opts.listen or [ anative ];
}
));
configFile = "${stateDir}/${flavor}-config.toml";
port = opts.port or 53;
in {
description = "trust-dns Domain Name Server (serving ${flavor})";
unitConfig.Documentation = "https://trust-dns.org/";
preStart = ''
wan=$(cat '${config.sane.services.dyn-dns.ipPath}')
${sed} s/%AWAN%/$wan/ ${configTemplate} > ${configFile}
'' + lib.optionalString (!opts ? config) ''
mkdir -p ${zoneDirFor flavor}
${sed} \
-e s/%CNAMENATIVE%/servo.${flavor}/ \
-e s/%ANATIVE%/${anative}/ \
-e s/%AWAN%/$wan/ \
-e s/%AOVPNS%/185.157.162.178/ \
${zoneTemplate} > ${zoneFor flavor}
'';
serviceConfig = config.systemd.services.trust-dns.serviceConfig // {
ExecStart = ''
${pkgs.trust-dns}/bin/${pkgs.trust-dns.meta.mainProgram} \
--port ${builtins.toString port} \
--zonedir ${zoneDirFor flavor}/ \
--config ${configFile} ${flagsStr}
'';
};
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
};
in {
trust-dns-wan = mkTrustDnsService { listen = [ nativeAddrs."servo.lan" bindOvpn ]; } "wan";
trust-dns-lan = mkTrustDnsService { port = 1053; } "lan";
trust-dns-hn = mkTrustDnsService { port = 1053; } "hn";
trust-dns-hn-resolver = mkTrustDnsService {
config = pkgs.writeText "hn-resolver-config.toml" ''
# i host a resolver in the wireguard VPN so that clients can resolve DNS through the VPN.
# (that's what this file achieves).
#
# one would expect this resolver could host the authoritative zone for `uninsane.org`, and then forward everything else to the system resolver...
# and while that works for `dig`, it breaks for `nslookup` (and so `ssh`, etc).
#
# DNS responses include a flag for if the responding server is the authority of the zone queried.
# it seems that default Linux stub resolvers either:
# - expect DNSSEC when the response includes that bit, or
# - expect A records to be in the `answer` section instead of `additional` section.
# or perhaps something more nuanced. but for `nslookup` to be reliable, it has to talk to an
# instance of trust-dns which is strictly a resolver, with no authority.
# hence, this config: a resolver which forwards to the actual authority.
listen_addrs_ipv4 = ["${nativeAddrs."servo.hn"}"]
listen_addrs_ipv6 = []
[[zones]]
zone = "uninsane.org"
zone_type = "Forward"
stores = { type = "forward", name_servers = [{ socket_addr = "${nativeAddrs."servo.hn"}:1053", protocol = "udp", trust_nx_responses = true }] }
[[zones]]
# forward the root zone to the local DNS resolver
zone = "."
zone_type = "Forward"
stores = { type = "forward", name_servers = [{ socket_addr = "127.0.0.53:53", protocol = "udp", trust_nx_responses = true }] }
'';
} "hn-resolver";
};
in
{
wan = {
substitutions = mkSubstitutions "wan";
listenAddrsIpv4 = [
nativeAddrs."servo.lan"
bindOvpn
];
};
lan = {
substitutions = mkSubstitutions "lan";
listenAddrsIpv4 = [ nativeAddrs."servo.lan" ];
port = 1053;
};
hn = {
substitutions = mkSubstitutions "hn";
listenAddrsIpv4 = [ nativeAddrs."servo.hn" ];
port = 1053;
};
# hn-resolver = {
# # don't need %AWAN% here because we forward to the hn instance.
# listenAddrsIpv4 = [ nativeAddrs."servo.hn" ];
# extraConfig = {
# zones = [
# {
# zone = "uninsane.org";
# zone_type = "Forward";
# stores = {
# type = "forward";
# name_servers = [
# {
# socket_addr = "${nativeAddrs."servo.hn"}:1053";
# protocol = "udp";
# trust_nx_responses = true;
# }
# ];
# };
# }
# {
# # forward the root zone to the local DNS resolver
# zone = ".";
# zone_type = "Forward";
# stores = {
# type = "forward";
# name_servers = [
# {
# socket_addr = "127.0.0.53:53";
# protocol = "udp";
# trust_nx_responses = true;
# }
# ];
# };
# }
# ];
# };
# };
};
sane.services.dyn-dns.restartOnChange = [
"trust-dns-wan.service"
@ -157,3 +207,4 @@ in
# "trust-dns-hn-resolver.service" # doesn't need restart because it doesn't know about WAN IP
];
}
]

View File

@ -81,14 +81,10 @@ let
(fromDb "feeds.simplecast.com/xKJ93w_w" // uncat) # Atlas Obscura
(fromDb "feeds.transistor.fm/acquired" // tech)
(fromDb "fulltimenix.com" // tech)
(fromDb "futureofcoding.org/episodes" // tech)
(fromDb "hackerpublicradio.org" // tech)
(fromDb "lexfridman.com/podcast" // rat)
(fromDb "mapspodcast.libsyn.com" // uncat) # Multidisciplinary Association for Psychedelic Studies
(fromDb "microarch.club" // tech)
(fromDb "omegataupodcast.net" // tech) # 3/4 German; 1/4 eps are English
(fromDb "omny.fm/shows/cool-people-who-did-cool-stuff" // pol) # Maggie Killjoy -- referenced by Cory Doctorow
(fromDb "omny.fm/shows/money-stuff-the-podcast") # Matt Levine
(fromDb "omny.fm/shows/the-dollop-with-dave-anthony-and-gareth-reynolds") # The Dollop history/comedy
(fromDb "originstories.libsyn.com" // uncat)
(fromDb "podcast.posttv.com/itunes/post-reports.xml" // pol)
@ -104,12 +100,9 @@ let
(fromDb "sharkbytes.transistor.fm" // tech) # Wireshark Podcast o_0
(fromDb "sscpodcast.libsyn.com" // rat) # Astral Codex Ten
(fromDb "talesfromthebridge.buzzsprout.com" // tech) # Sci-Fi? has Peter Watts; author of No Moods, Ads or Cutesy Fucking Icons (rifters.com)
(fromDb "theamphour.com" // tech)
(fromDb "techtalesshow.com" // tech) # Corbin Davenport
(fromDb "techwontsave.us" // pol) # rec by Cory Doctorow
(fromDb "wakingup.libsyn.com" // pol) # Sam Harris
(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.megaphone.fm/hubermanlab" // uncat) # Daniel Huberman on sleep
@ -129,11 +122,11 @@ let
(fromDb "acoup.blog/feed") # history, states. author: <https://historians.social/@bretdevereaux/following>
(fromDb "amosbbatto.wordpress.com" // tech)
(fromDb "anish.lakhwara.com" // tech)
(fromDb "apenwarr.ca/log/rss.php" // tech) # CEO of tailscale
(fromDb "applieddivinitystudies.com" // rat)
(fromDb "artemis.sh" // tech)
(fromDb "ascii.textfiles.com" // tech) # Jason Scott
(fromDb "austinvernon.site" // tech)
# (fromDb "balajis.com" // pol) # Balaji
(fromDb "ben-evans.com/benedictevans" // pol)
(fromDb "bitbashing.io" // tech)
(fromDb "bitsaboutmoney.com" // uncat)
@ -144,19 +137,20 @@ let
(fromDb "blog.thalheim.io" // tech) # Mic92
(fromDb "bunniestudios.com" // tech) # Bunnie Juang
(fromDb "capitolhillseattle.com" // pol)
# (fromDb "drewdevault.com" // tech)
# (fromDb "econlib.org" // pol)
(fromDb "edwardsnowden.substack.com" // pol // text)
(fromDb "fasterthanli.me" // tech)
(fromDb "gwern.net" // rat)
(fromDb "hardcoresoftware.learningbyshipping.com" // tech) # Steven Sinofsky
(fromDb "harihareswara.net" // tech // pol) # rec by Cory Doctorow
(fromDb "ianthehenry.com" // tech)
(fromDb "idiomdrottning.org" // uncat)
(fromDb "interconnected.org/home/feed" // rat) # Matt Webb -- engineering-ish, but dreamy
(fromDb "jeffgeerling.com" // tech)
(fromDb "jefftk.com" // tech)
(fromDb "jwz.org/blog" // tech // pol) # DNA lounge guy, loooong-time blogger
(fromDb "kill-the-newsletter.com/feeds/joh91bv7am2pnznv.xml" // pol) # Matt Levine - Money Stuff
(fromDb "kosmosghost.github.io/index.xml" // tech)
# (fromDb "lesswrong.com" // rat)
(fromDb "linmob.net" // tech)
(fromDb "lwn.net" // tech)
(fromDb "lynalden.com" // pol)
@ -164,20 +158,19 @@ let
(fromDb "mg.lol" // tech)
(fromDb "mindingourway.com" // rat)
(fromDb "morningbrew.com/feed" // pol)
(fromDb "nixpkgs.news" // tech)
(fromDb "overcomingbias.com" // rat) # Robin Hanson
(fromDb "palladiummag.com" // uncat)
(fromDb "philosopher.coach" // rat) # Peter Saint-Andre -- side project of stpeter.im
(fromDb "pomeroyb.com" // tech)
(fromDb "postmarketos.org/blog" // tech)
(fromDb "preposterousuniverse.com" // rat) # Sean Carroll
(fromDb "profectusmag.com" // uncat)
(fromDb "project-insanity.org" // tech) # shared blog by a few NixOS devs, notably onny
(fromDb "putanumonit.com" // rat) # mostly dating topics. not advice, or humor, but looking through a social lens
(fromDb "richardcarrier.info" // rat)
(fromDb "rifters.com/crawl" // uncat) # No Moods, Ads or Cutesy Fucking Icons
(fromDb "righto.com" // tech) # Ken Shirriff
(fromDb "rootsofprogress.org" // rat) # Jason Crawford
(fromDb "samuel.dionne-riel.com" // tech) # SamuelDR
(fromDb "sagacioussuricata.com" // tech) # ian (Sanctuary)
(fromDb "semiaccurate.com" // tech)
(fromDb "sideways-view.com" // rat) # Paul Christiano
@ -186,40 +179,31 @@ let
(fromDb "spectrum.ieee.org" // tech)
(fromDb "stpeter.im/atom.xml" // pol)
(fromDb "thediff.co" // pol) # Byrne Hobart
# (fromDb "theregister.com" // tech)
(fromDb "thisweek.gnome.org" // tech)
(fromDb "tuxphones.com" // tech)
(fromDb "uninsane.org" // tech)
(fromDb "unintendedconsequenc.es" // rat)
# (fromDb "vitalik.ca" // tech) # moved to vitalik.eth.limo
(fromDb "vitalik.eth.limo" // tech) # Vitalik Buterin
(fromDb "willow.phantoma.online") # wizard@xyzzy.link
# (fromDb "webcurious.co.uk" // uncat) # link aggregator; defunct?
(fromDb "xn--gckvb8fzb.com" // tech)
(mkSubstack "astralcodexten" // rat // daily) # Scott Alexander
# (mkSubstack "doomberg" // tech // weekly) # articles are all pay-walled
(mkSubstack "eliqian" // rat // weekly)
(mkSubstack "oversharing" // pol // daily)
(mkSubstack "samkriss" // humor // infrequent)
(mkText "http://benjaminrosshoffman.com/feed" // pol // weekly)
(mkText "http://boginjr.com/feed" // tech // infrequent)
(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://jvns.ca/atom.xml" // tech // weekly) # Julia Evans
(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/stories-rss.xml" // tech // weekly)
(mkText "https://solar.lowtechmagazine.com/posts/index.xml" // tech // weekly)
(mkText "https://www.stratechery.com/rss" // pol // weekly) # Ben Thompson
# (fromDb "balajis.com" // pol) # Balaji
# (fromDb "drewdevault.com" // tech)
# (fromDb "econlib.org" // pol)
# (fromDb "lesswrong.com" // rat)
# (fromDb "profectusmag.com" // pol) # some conservative/libertarian think tank
# (fromDb "thesideview.co" // uncat) # spiritual journal; RSS items are stubs
# (fromDb "theregister.com" // tech)
# (fromDb "vitalik.ca" // tech) # moved to vitalik.eth.limo
# (fromDb "webcurious.co.uk" // uncat) # link aggregator; defunct?
# (mkSubstack "doomberg" // tech // weekly) # articles are all pay-walled
# (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
# (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.stratechery.com/rss" // pol // weekly) # Ben Thompson
];
videos = [
@ -231,20 +215,17 @@ let
(fromDb "youtube.com/@JackStauber")
(fromDb "youtube.com/@NativLang")
(fromDb "youtube.com/@PolyMatter")
# (fromDb "youtube.com/@rossmanngroup" // pol // tech) # Louis Rossmann
(fromDb "youtube.com/@TechnologyConnections" // tech)
(fromDb "youtube.com/@TheB1M")
(fromDb "youtube.com/@TomScottGo")
(fromDb "youtube.com/@Vihart")
(fromDb "youtube.com/@Vox")
(fromDb "youtube.com/@Vsauce")
# (fromDb "youtube.com/@rossmanngroup" // pol // tech) # Louis Rossmann
];
images = [
(fromDb "catandgirl.com" // img // humor)
(fromDb "davidrevoy.com" // img // art)
(fromDb "grumpy.website" // img // humor)
(fromDb "miniature-calendar.com" // img // art // daily)
(fromDb "pbfcomics.com" // img // humor)
(fromDb "poorlydrawnlines.com/feed" // img // humor)

View File

@ -2,7 +2,7 @@
# - x-systemd options: <https://www.freedesktop.org/software/systemd/man/systemd.mount.html>
# - fuse options: `man mount.fuse`
{ config, lib, pkgs, sane-lib, utils, ... }:
{ lib, pkgs, sane-lib, ... }:
let
fsOpts = rec {
@ -23,15 +23,15 @@ let
# N.B.: `remote-fs.target` is a dependency of multi-user.target, itself of graphical.target.
# hence, omitting `noauto` can slow down boots.
noauto = [ "noauto" ];
# lazyMount: defer mounting until first access from userspace.
# see: `man systemd.automount`, `man automount`, `man autofs`
# lazyMount: defer mounting until first access from userspace
lazyMount = noauto ++ automount;
wg = [
"x-systemd.requires=wireguard-wg-home.service"
"x-systemd.after=wireguard-wg-home.service"
];
fuse = [
ssh = common ++ [
"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_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.
@ -44,18 +44,7 @@ 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.
"default_permissions"
];
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 ++ [
sshColin = ssh ++ [
# 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.
# symlinks which point outside the mount ARE followed. so this is more capable than `transform_symlinks`
@ -63,6 +52,9 @@ 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.
# only symlinks which would point inside the mountpoint are translated.
"transform_symlinks"
"idmap=user"
"uid=1000"
"gid=100"
];
# sshRoot = ssh ++ [
# # we don't transform_symlinks because that breaks the validity of remote /nix stores
@ -75,44 +67,21 @@ let
# actimeo=n = how long (in seconds) to cache file/dir attributes (default: 3-60s)
# bg = retry failed mounts in the background
# 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
# 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)
# 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.
# 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 ++ [
# "actimeo=5"
# "bg"
"retrans=1"
# "actimeo=10"
"bg"
"retrans=4"
"retry=0"
# "intr"
"soft"
"softreval"
"timeo=30"
"timeo=15"
"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: casting shows to T.V. fails partway through about half the time
"connect_timeout=20"
];
};
remoteHome = host: {
sane.programs.sshfs-fuse.enableFor.system = true;
fileSystems."/mnt/${host}/home" = {
device = "colin@${host}:/home/colin";
fsType = "fuse.sshfs";
@ -125,54 +94,6 @@ let
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
lib.mkMerge [
{
@ -207,6 +128,35 @@ 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)
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 = [
# # 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
@ -214,23 +164,13 @@ lib.mkMerge [
# ];
programs.fuse.userAllowOther = true; #< necessary for `allow_other` or `allow_root` options.
environment.systemPackages = [
pkgs.sshfs-fuse
];
}
(remoteHome "desko")
(remoteHome "lappy")
(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/games")
(remoteServo "media/Music")
(remoteServo "media/Pictures/macros")
(remoteServo "media/torrents")
(remoteServo "media/Videos")
(remoteServo "playground")
]

View File

@ -35,16 +35,6 @@
# servo needs zfs though, which doesn't support every kernel.
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.
boot.initrd.preFailCommands = "allowShell=1";
@ -80,12 +70,14 @@
# - query details with `sudo cpupower frequency-info`
powerManagement.cpuFreqGovernor = "ondemand";
# see: `man logind.conf`
# dont shutdown when power button is short-pressed (commonly done an accident, or by cats).
# but do on long-press: useful to gracefully power-off server.
services.logind.powerKey = "lock";
services.logind.powerKeyLongPress = "poweroff";
services.logind.lidSwitch = "lock";
services.logind.extraConfig = ''
# see: `man logind.conf`
# dont shutdown when power button is short-pressed (commonly done an accident, or by cats).
# but do on long-press: useful to gracefully power-off server.
HandlePowerKey=lock
HandlePowerKeyLongPress=poweroff
HandleLidSwitch=lock
'';
# services.snapper.configs = {
# root = {

View File

@ -3,18 +3,13 @@
{
# XDG defines things like ~/Desktop, ~/Downloads, etc.
# these clutter the home, so i mostly don't use them.
# note that several of these are not actually standardized anywhere.
# some are even non-conventional, like:
# - XDG_PHOTOS_DIR: only works because i patch e.g. megapixels
sane.user.fs.".config/user-dirs.dirs".symlink.text = ''
XDG_DESKTOP_DIR="$HOME/.xdg/Desktop"
XDG_DOCUMENTS_DIR="$HOME/dev"
XDG_DOWNLOAD_DIR="$HOME/tmp"
XDG_MUSIC_DIR="$HOME/Music"
XDG_PHOTOS_DIR="$HOME/Pictures/Photos"
XDG_PICTURES_DIR="$HOME/Pictures"
XDG_PUBLICSHARE_DIR="$HOME/.xdg/Public"
XDG_SCREENSHOTS_DIR="$HOME/Pictures/Screenshots"
XDG_TEMPLATES_DIR="$HOME/.xdg/Templates"
XDG_VIDEOS_DIR="$HOME/Videos"
'';
@ -22,12 +17,4 @@
# prevent `xdg-user-dirs-update` from overriding/updating our config
# 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.".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
'';
}

View File

@ -60,5 +60,8 @@
networking.networkmanager.plugins = lib.mkForce [];
# keyfile.path = where networkmanager should look for connection credentials
networking.networkmanager.settings.keyfile.path = "/var/lib/NetworkManager/system-connections";
networking.networkmanager.extraConfig = ''
[keyfile]
path=/var/lib/NetworkManager/system-connections
'';
}

View File

@ -19,15 +19,10 @@
#
# namespacing:
# - each namespace can use a different /etc/resolv.conf to specify different DNS servers (see `firejail --dns=...`)
# - nscd breaks namespacing: the host nscd is unaware of the guest's /etc/resolv.conf, and so directs the guest's DNS requests to the host's servers.
# - nscd breaks namespacing: the host nscd is unaware of the guest's /etc/resolv.conf, and so direct's the guest's DNS requests to the host's servers.
# - this is fixed by either `firejail --blacklist=/var/run/nscd/socket`, or disabling nscd altogether.
{ config, lib, ... }:
lib.mkMerge [
{ lib, ... }:
{
sane.services.trust-dns.enable = lib.mkDefault config.sane.services.trust-dns.asSystemResolver;
sane.services.trust-dns.asSystemResolver = lib.mkDefault true;
}
(lib.mkIf (!config.sane.services.trust-dns.asSystemResolver) {
# use systemd's stub resolver.
# /etc/resolv.conf isn't sophisticated enough to use different servers per net namespace (or link).
# instead, running the stub resolver on a known address in the root ns lets us rewrite packets
@ -49,8 +44,7 @@ lib.mkMerge [
# stub resolver (just forwards upstream) lives on 127.0.0.54
"127.0.0.53"
];
})
{
# nscd -- the Name Service Caching Daemon -- caches DNS query responses
# in a way that's unaware of my VPN routing, so routes are frequently poor against
# services which advertise different IPs based on geolocation.
@ -71,4 +65,3 @@ lib.mkMerge [
services.nscd.enable = false;
system.nssModules = lib.mkForce [];
}
]

View File

@ -53,7 +53,7 @@
# 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`.
nix.nixPath = (lib.optionals (config.sane.maxBuildCost >= 2) [
nix.nixPath = (lib.optionals config.sane.enableSlowPrograms [
"nixpkgs=${pkgs.path}"
]) ++ [
# note the import starts at repo root: this allows `./overlay/default.nix` to access the stuff at the root
@ -65,10 +65,10 @@
# ensure new deployments have a source of this repo with which they can bootstrap.
# this however changes on every commit and can be slow to copy for e.g. `moby`.
environment.etc."nixos" = lib.mkIf (config.sane.maxBuildCost >= 3) {
environment.etc."nixos" = lib.mkIf config.sane.enableSlowPrograms {
source = ../../..;
};
environment.etc."nix/registry.json" = lib.mkIf (config.sane.maxBuildCost < 3) {
environment.etc."nix/registry.json" = lib.mkIf (!config.sane.enableSlowPrograms) {
enable = false;
};

View File

@ -64,9 +64,4 @@
# 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)";
}

View File

@ -87,8 +87,13 @@ in
services.abaddon = {
description = "unofficial Discord chat client";
partOf = lib.mkIf cfg.config.autostart [ "graphical-session" ];
command = "abaddon";
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/abaddon";
Type = "simple";
Restart = "always";
RestartSec = "20s";
};
};
};
}

View File

@ -35,15 +35,15 @@ in
# 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'
--replace 'HiFi.conf' '/Allwinner/A64/PinePhone/HiFi.conf'
substituteInPlace ucm2/Allwinner/A64/PinePhone/PinePhone.conf \
--replace-fail 'VoiceCall.conf' '/Allwinner/A64/PinePhone/VoiceCall.conf'
--replace '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'
substituteInPlace ucm2/Allwinner/A64/PinePhone/* \
--replace 'PlaybackPriority 300' 'PlaybackPriority 100'
'';
});

View File

@ -30,8 +30,6 @@
});
};
buildCost = 1;
sandbox.method = "bwrap";
sandbox.whitelistWayland = true;

View File

@ -63,7 +63,6 @@ in
"jq"
"killall"
"less"
"lftp"
# "libcap_ng" # for `netcap`
"lsof"
# "miniupnpc"
@ -80,7 +79,6 @@ in
"powertop"
"pstree"
"ripgrep"
"s6-rc" # service manager
"screen"
"smartmontools" # smartctl
# "socat"
@ -90,7 +88,6 @@ in
"tree"
"usbutils" # lsusb
"util-linux" # lsblk, lscpu, etc
"valgrind"
"wget"
"wirelesstools" # iwlist
# "xq" # jq for XML
@ -110,6 +107,7 @@ in
# - debugging?
consoleUtils = declPackageSet [
"alsaUtils" # for aplay, speaker-test
"strings"
# "cdrtools"
# "clinfo"
# "dmidecode"
@ -124,7 +122,6 @@ in
# "gopass"
# "gopass-jsonapi"
# "helix" # text editor
"htop" # needed as a user package, for ~/.config/htop
# "libsecret" # for managing user keyrings (secret-tool)
# "lm_sensors" # for sensors-detect
# "lshw"
@ -138,7 +135,6 @@ in
"nmon"
# "node2nix"
# "oathToolkit" # for oathtool
"objdump"
# "ponymix"
"pulsemixer"
"python3-repl"
@ -151,7 +147,6 @@ in
"sops" # for manually viewing secrets; outside `sane-secrets` (TODO: improve sane-secrets!)
"speedtest-cli"
# "ssh-to-age"
"strings"
"sudo"
# "tageditor" # music tagging
# "unar"
@ -176,7 +171,6 @@ in
"ffmpeg"
"go2tv" # cast videos to UPNP/DLNA device (i.e. tv).
"imagemagick"
"sane-cast" # cast videos to UPNP/DLNA, with compatibility
"sox"
"yt-dlp"
];
@ -215,7 +209,6 @@ in
backblaze-b2 = {};
blanket.buildCost = 1;
blanket.sandbox.method = "bwrap";
blanket.sandbox.whitelistAudio = true;
# blanket.sandbox.whitelistDbus = [ "user" ]; # TODO: untested
@ -268,42 +261,34 @@ in
ddrescue.sandbox.method = "landlock"; # TODO:sandbox: untested
ddrescue.sandbox.autodetectCliPaths = "existingOrParent";
delfin.buildCost = 1;
# auth token, preferences
delfin.sandbox.method = "bwrap";
delfin.sandbox.whitelistAudio = true;
delfin.sandbox.whitelistDbus = [ "user" ]; # else `mpris` plugin crashes the player
delfin.sandbox.whitelistDri = true;
delfin.sandbox.whitelistWayland = true;
delfin.sandbox.net = "clearnet";
# auth token, preferences
delfin.persist.byStore.private = [ ".config/delfin" ];
dig.sandbox.method = "bwrap";
dig.sandbox.net = "all";
# creds, but also 200 MB of node modules, etc
discord.persist.byStore.private = [ ".config/discord" ];
discord.suggestedPrograms = [ "xwayland" ];
discord.sandbox.method = "bwrap";
discord.sandbox.wrapperType = "inplace"; #< /opt-style packaging
discord.sandbox.whitelistAudio = true;
discord.sandbox.whitelistDbus = [ "user" ]; # needed for xdg-open
discord.sandbox.whitelistWayland = true;
discord.sandbox.whitelistX = true;
discord.sandbox.net = "clearnet";
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"
];
discord.persist.byStore.private = [ ".config/discord" ];
dtc.sandbox.method = "bwrap";
dtc.sandbox.autodetectCliPaths = true; # TODO:sandbox: untested
dtrx.sandbox.method = "bwrap";
dtrx.sandbox.whitelistPwd = true;
dtrx.sandbox.autodetectCliPaths = "existing"; #< for the archive
duplicity = {};
e2fsprogs.sandbox.method = "landlock";
@ -316,13 +301,11 @@ in
eg25-control = {};
electrum.buildCost = 1;
electrum.sandbox.method = "bwrap"; # TODO:sandbox: untested
electrum.sandbox.net = "all"; # TODO: probably want to make this run behind a VPN, always
electrum.sandbox.whitelistWayland = true;
electrum.persist.byStore.cryptClearOnBoot = [ ".electrum" ]; #< TODO: use XDG dirs!
endless-sky.buildCost = 1;
endless-sky.persist.byStore.plaintext = [ ".local/share/endless-sky" ];
endless-sky.sandbox.method = "bwrap";
endless-sky.sandbox.whitelistAudio = true;
@ -361,7 +344,6 @@ in
".persist/plaintext"
];
ffmpeg.buildCost = 1;
ffmpeg.sandbox.method = "bwrap";
ffmpeg.sandbox.autodetectCliPaths = "existingFileOrParent"; # it outputs uncreated files -> parent dir needs mounting
@ -379,9 +361,7 @@ in
fluffychat-moby.persist.byStore.plaintext = [ ".local/share/chat.fluffy.fluffychat" ];
font-manager.buildCost = 1;
font-manager.sandbox.method = "bwrap";
font-manager.sandbox.whitelistWayland = true;
font-manager.packageUnwrapped = pkgs.rmDbusServicesInPlace (pkgs.font-manager.override {
# build without the "Google Fonts" integration feature, to save closure / avoid webkitgtk_4_0
withWebkit = false;
@ -389,7 +369,7 @@ in
forkstat.sandbox.method = "landlock"; #< doesn't seem to support bwrap
forkstat.sandbox.extraConfig = [
"--sanebox-keep-namespace" "pid"
"--sane-sandbox-keep-namespace" "pid"
];
forkstat.sandbox.extraPaths = [
"/proc"
@ -416,9 +396,8 @@ in
# TODO: we can populate gh's stuff statically; it even lets us use the same oauth across machines
gh.persist.byStore.private = [ ".config/gh" ];
gimp.buildCost = 1;
gimp.sandbox.method = "bwrap";
gimp.sandbox.whitelistX = true;
gimp.sandbox.net = "clearnet"; #< for Xwayland
gimp.sandbox.whitelistWayland = true;
gimp.sandbox.extraHomePaths = [
"Pictures/albums"
@ -436,39 +415,32 @@ in
"/tmp" # "Cannot open display:" if it can't mount /tmp 👀
];
"gnome.gnome-calculator".buildCost = 1;
"gnome.gnome-calculator".sandbox.method = "bwrap";
"gnome.gnome-calculator".sandbox.whitelistWayland = true;
"gnome.gnome-calendar".buildCost = 1;
# 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.whitelistWayland = true;
"gnome.gnome-clocks".sandbox.method = "bwrap";
"gnome.gnome-clocks".sandbox.whitelistWayland = true;
"gnome.gnome-clocks".suggestedPrograms = [ "dconf" ];
# gnome-disks
"gnome.gnome-disk-utility".buildCost = 1;
"gnome.gnome-disk-utility".sandbox.method = "bwrap";
"gnome.gnome-disk-utility".sandbox.whitelistDbus = [ "system" ];
"gnome.gnome-disk-utility".sandbox.whitelistWayland = true;
"gnome.gnome-disk-utility".sandbox.extraHomePaths = [
"tmp"
"use/iso"
# TODO: probably need /dev and such
];
# seahorse: dump gnome-keyring secrets.
"gnome.seahorse".buildCost = 1;
# 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.whitelistDbus = [ "user" ];
"gnome.seahorse".sandbox.whitelistWayland = true;
gnome-2048.buildCost = 1;
gnome-2048.sandbox.method = "bwrap";
gnome-2048.sandbox.whitelistWayland = true;
gnome-2048.persist.byStore.plaintext = [ ".local/share/gnome-2048/scores" ];
gnome-frog.buildCost = 1;
gnome-frog.sandbox.method = "bwrap";
gnome-frog.sandbox.whitelistWayland = true;
gnome-frog.sandbox.whitelistDbus = [ "user" ];
@ -495,7 +467,6 @@ in
# 1. no number may appear unshaded more than once in the same row/column
# 2. no two shaded tiles can be direct N/S/E/W neighbors
# - win once (1) and (2) are satisfied
"gnome.hitori".buildCost = 1;
"gnome.hitori".sandbox.method = "bwrap";
"gnome.hitori".sandbox.whitelistWayland = true;
@ -525,7 +496,6 @@ in
grim.sandbox.autodetectCliPaths = "existingOrParent";
grim.sandbox.whitelistWayland = true;
hase.buildCost = 1;
hase.sandbox.method = "bwrap";
hase.sandbox.net = "clearnet";
hase.sandbox.whitelistAudio = true;
@ -539,6 +509,16 @@ in
host.sandbox.method = "landlock";
host.sandbox.net = "all"; #< technically, only needs to contact localhost's DNS server
htop.sandbox.method = "landlock";
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.capabilities = [ "net_raw" ];
@ -546,7 +526,6 @@ in
# N.B.: inetutils' `ping` is shadowed by iputils' ping (by nixos, intentionally).
inetutils.sandbox.method = "landlock"; # want to keep the same netns, at least.
inkscape.buildCost = 1;
inkscape.sandbox.method = "bwrap";
inkscape.sandbox.whitelistWayland = true;
inkscape.sandbox.extraHomePaths = [
@ -572,10 +551,6 @@ in
iproute2.sandbox.method = "landlock";
iproute2.sandbox.net = "all";
iproute2.sandbox.capabilities = [ "net_admin" ];
iproute2.sandbox.extraPaths = [
"/run/netns" # for `ip netns ...` to work
"/var/run/netns"
];
iptables.sandbox.method = "landlock";
iptables.sandbox.net = "all";
@ -598,7 +573,6 @@ in
"/proc"
];
krita.buildCost = 1;
krita.sandbox.method = "bwrap";
krita.sandbox.whitelistWayland = true;
krita.sandbox.autodetectCliPaths = "existing";
@ -619,7 +593,6 @@ in
libnotify.sandbox.method = "bwrap";
libnotify.sandbox.whitelistDbus = [ "user" ]; # notify-send
losslesscut-bin.buildCost = 1;
losslesscut-bin.sandbox.method = "bwrap";
losslesscut-bin.sandbox.extraHomePaths = [
"Music"
@ -635,16 +608,27 @@ in
losslesscut-bin.sandbox.whitelistX = true;
lsof.sandbox.method = "capshonly"; # lsof doesn't sandbox under bwrap or even landlock w/ full access to /
lsof.sandbox.capabilities = [ "dac_override" "sys_ptrace" ];
lua = {};
"mate.engrampa".packageUnwrapped = pkgs.rmDbusServices pkgs.mate.engrampa;
"mate.engrampa".sandbox.method = "bwrap"; # TODO:sandbox: untested
"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.net = "clearnet";
mercurial.sandbox.whitelistPwd = true;
# actual monero blockchain (not wallet/etc; safe to delete, just slow to regenerate)
monero-gui.buildCost = 1;
# 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.sandbox.method = "bwrap";
@ -653,7 +637,6 @@ in
"records/finance/cryptocurrencies/monero"
];
mumble.buildCost = 1;
mumble.persist.byStore.private = [ ".local/share/Mumble" ];
nano.sandbox.method = "bwrap";
@ -744,7 +727,7 @@ in
# procps: free, pgrep, pidof, pkill, ps, pwait, top, uptime, couple others
procps.sandbox.method = "bwrap";
procps.sandbox.extraConfig = [
"--sanebox-keep-namespace" "pid"
"--sane-sandbox-keep-namespace" "pid"
];
pstree.sandbox.method = "landlock";
@ -757,14 +740,11 @@ in
pulsemixer.sandbox.method = "landlock";
pulsemixer.sandbox.whitelistAudio = true;
pwvucontrol.buildCost = 1;
pwvucontrol.sandbox.method = "bwrap";
pwvucontrol.sandbox.whitelistAudio = true;
pwvucontrol.sandbox.whitelistDri = true; # else perf on moby is unusable
pwvucontrol.sandbox.whitelistWayland = true;
python3-repl.packageUnwrapped = pkgs.python3.withPackages (ps: with ps; [
psutil
requests
]);
python3-repl.sandbox.method = "bwrap";
@ -775,7 +755,7 @@ in
];
qemu.sandbox.enable = false; #< it's a launcher
qemu.buildCost = 2;
qemu.slowToBuild = true;
rsync.sandbox.method = "bwrap";
rsync.sandbox.net = "clearnet";
@ -783,9 +763,10 @@ in
rustc = {};
sane-cast = {}; #< TODO: sandbox this the same way i sandbox go2tv
sane-die-with-parent.sandbox.enable = false; #< it's a launcher; can't sandbox
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
@ -793,7 +774,6 @@ in
sequoia.sandbox.whitelistPwd = true;
sequoia.sandbox.autodetectCliPaths = true;
shattered-pixel-dungeon.buildCost = 1;
shattered-pixel-dungeon.persist.byStore.plaintext = [ ".local/share/.shatteredpixel/shattered-pixel-dungeon" ];
shattered-pixel-dungeon.sandbox.method = "bwrap";
shattered-pixel-dungeon.sandbox.whitelistAudio = true;
@ -801,7 +781,6 @@ in
shattered-pixel-dungeon.sandbox.whitelistWayland = true;
# printer/filament settings
slic3r.buildCost = 1;
slic3r.persist.byStore.plaintext = [ ".Slic3r" ];
slurp.sandbox.method = "bwrap";
@ -816,13 +795,12 @@ in
sops.sandbox.method = "bwrap"; # TODO:sandbox: untested
sops.sandbox.extraHomePaths = [
".config/sops"
"nixos"
"dev/nixos"
# TODO: sops should only need access to knowledge/secrets,
# except that i currently put its .sops.yaml config in the root of ~/knowledge
"knowledge"
];
soundconverter.buildCost = 1;
soundconverter.sandbox.method = "bwrap";
soundconverter.sandbox.whitelistWayland = true;
soundconverter.sandbox.extraHomePaths = [
@ -840,7 +818,6 @@ in
sox.sandbox.autodetectCliPaths = "existingFileOrParent";
sox.sandbox.whitelistAudio = true;
space-cadet-pinball.buildCost = 1;
space-cadet-pinball.persist.byStore.plaintext = [ ".local/share/SpaceCadetPinball" ];
space-cadet-pinball.sandbox.method = "bwrap";
space-cadet-pinball.sandbox.whitelistAudio = true;
@ -852,8 +829,6 @@ in
sqlite = {};
sshfs-fuse = {}; # used by fs.nix
strace.sandbox.enable = false; #< needs to `exec` its args, and therefore support *anything*
subversion.sandbox.method = "bwrap";
@ -861,7 +836,6 @@ in
subversion.sandbox.whitelistPwd = true;
sudo.sandbox.enable = false;
superTux.buildCost = 1;
superTux.sandbox.method = "bwrap";
superTux.sandbox.wrapperType = "inplace"; # package Makefile incorrectly installs to $out/games/superTux instead of $out/share/games
superTux.sandbox.whitelistAudio = true;
@ -880,14 +854,12 @@ in
tdesktop.persist.byStore.private = [ ".local/share/TelegramDesktop" ];
tokodon.buildCost = 1;
tokodon.persist.byStore.private = [ ".cache/KDE/tokodon" ];
tree.sandbox.method = "landlock";
tree.sandbox.autodetectCliPaths = true;
tree.sandbox.whitelistPwd = true;
tumiki-fighters.buildCost = 1;
tumiki-fighters.sandbox.method = "bwrap";
tumiki-fighters.sandbox.whitelistAudio = true;
tumiki-fighters.sandbox.whitelistDri = true; #< not strictly necessary, but triples CPU perf
@ -906,16 +878,12 @@ in
"/sys/bus/usb"
];
valgrind.buildCost = 1;
valgrind.sandbox.enable = false; #< it's a launcher: can't sandbox
visidata.sandbox.method = "bwrap"; # TODO:sandbox: untested
visidata.sandbox.autodetectCliPaths = true;
# `vulkaninfo`, `vkcube`
vulkan-tools.sandbox.method = "landlock";
vvvvvv.buildCost = 1;
vvvvvv.sandbox.method = "bwrap";
vvvvvv.sandbox.whitelistAudio = true;
vvvvvv.sandbox.whitelistDri = true; #< playable without, but burns noticably more CPU
@ -936,7 +904,6 @@ in
wget.sandbox.net = "all";
wget.sandbox.whitelistPwd = true; # saves to pwd by default
whalebird.buildCost = 1;
whalebird.persist.byStore.private = [ ".config/Whalebird" ];
# `wg`, `wg-quick`
@ -955,7 +922,7 @@ in
xwayland.sandbox.method = "bwrap";
xwayland.sandbox.wrapperType = "inplace"; #< consumers use it as a library (e.g. wlroots)
xwayland.sandbox.whitelistWayland = true; #< just assuming this is needed
xwayland.sandbox.whitelistX = true;
xwayland.sandbox.net = "clearnet"; #< just assuming this is needed (X11 traffic)
xwayland.sandbox.whitelistDri = true; #< would assume this gives better gfx perf
xterm.sandbox.enable = false; # need to be able to do everything

View File

@ -1,8 +1,3 @@
# tips/tricks
# - audio recording
# - default recording input will be silent, on lappy.
# - Audio Setup -> Rescan Audio Devices ...
# - Audio Setup -> Recording device -> sysdefault
{ pkgs, ... }:
{
sane.programs.audacity = {
@ -14,8 +9,6 @@
};
};
buildCost = 1;
sandbox.method = "bwrap";
sandbox.whitelistAudio = true;
sandbox.whitelistWayland = true;
@ -27,9 +20,6 @@
# audacity needs the entire config dir mounted if running in a sandbox
".config/audacity"
];
sandbox.extraPaths = [
"/dev/snd" # for recording audio inputs to work
];
# disable first-run splash screen
fs.".config/audacity/audacity.cfg".file.text = ''

View File

@ -2,7 +2,10 @@
#!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
@ -13,9 +16,53 @@ logger = logging.getLogger(__name__)
# map from known devices -> required flags
DEVICE_MAP = {
"Theater TV": [],
"Cuddlevision": [ "-usewav" ],
"[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
@ -49,6 +96,8 @@ class BlastDriver:
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 = []
@ -153,11 +202,15 @@ 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()

View File

@ -42,9 +42,9 @@ in
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.
"--sanebox-keep-namespace" "pid"
"--sane-sandbox-keep-namespace" "pid"
];
suggestedPrograms = [ "blast-ugjka" "sane-die-with-parent" ];
suggestedPrograms = [ "blast-ugjka" ];
};
networking.firewall.allowedTCPPorts = lib.mkIf cfg.enabled [ 9000 ];

View File

@ -99,12 +99,18 @@ in
type = types.listOf transitionType;
default = [];
};
configFile = mkOption {
type = types.path;
default = pkgs.writeText "bonsai_tree.json" (builtins.toJSON cfg.config.transitions);
description = ''
configuration file to pass to bonsai.
usually auto-generated from the sibling options; exposed mainly for debugging or convenience.
'';
};
};
};
};
fs.".config/bonsai/bonsai_tree.json".symlink.text = builtins.toJSON cfg.config.transitions;
sandbox.method = "bwrap";
sandbox.extraRuntimePaths = [
"/" #< just needs "bonsai", but needs to create it first...
@ -112,10 +118,18 @@ in
services.bonsaid = {
description = "bonsai: programmable input dispatcher";
partOf = [ "graphical-session" ];
# nice -n -11 chosen arbitrarily. i hope this will allow for faster response to inputs, but without audio underruns (pipewire is -21, dino -15-ish)
command = "nice -n -11 bonsaid -t $HOME/.config/bonsai/bonsai_tree.json";
cleanupCommand = "rm -f $XDG_RUNTIME_DIR/bonsai";
after = [ "graphical-session.target" ];
wantedBy = [ "graphical-session.target" ];
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";
};
};
};
}

View File

@ -21,12 +21,12 @@
# 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
# never receive capabilities in their parent namespace.
substituteInPlace bubblewrap.c --replace-fail \
substituteInPlace bubblewrap.c --replace \
'die ("Unexpected capabilities but not setuid, old file caps config?");' \
'// die ("Unexpected capabilities but not setuid, old file caps config?");'
# enable debug printing
# substituteInPlace utils.h --replace-fail \
# substituteInPlace utils.h --replace \
# '#define __debug__(x)' \
# '#define __debug__(x) printf x'
'';

View File

@ -7,7 +7,7 @@
# - message @cheogram.com "reset sip account" (this is not destructive, despite the name)
# - the bot will reply with auto-generated username/password plus a SIP server endpoint.
# just copy those into gnome-calls' GUI configurator
# - now gnome-calls can do outbound calls. inbound calls can be routed by messaging the bot: "configure calls"
# - now gnome-calls can do outbound calls. inbound calls requires more chatting with the help bot
#
# my setup here is still very WIP.
# open questions:
@ -25,7 +25,7 @@ in
type = types.submodule {
options.autostart = mkOption {
type = types.bool;
default = true;
default = false;
};
};
};
@ -36,19 +36,23 @@ in
".local/share/calls" # call "records"
# .local/share/folks # contacts?
];
# this is only the username/endpoint: the actual password appears to be stored in gnome-keyring
secrets.".config/calls/sip-account.cfg" = ../../../secrets/common/gnome_calls_sip-account.cfg.bin;
suggestedPrograms = [
"feedbackd" # needs `phone-incoming-call`, in particular
"gnome-keyring" # to remember the password
];
services.gnome-calls = {
# TODO: prevent gnome-calls from daemonizing when started manually
description = "gnome-calls daemon to monitor incoming SIP calls";
partOf = lib.mkIf cfg.config.autostart [ "graphical-session" ];
# add --verbose for more debugging
command = "env G_MESSAGES_DEBUG=all gnome-calls --daemon";
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
serviceConfig = {
# add --verbose for more debugging
ExecStart = "${cfg.package}/bin/gnome-calls --daemon";
Type = "simple";
Restart = "always";
RestartSec = "10s";
};
environment.G_MESSAGES_DEBUG = "all";
};
};
programs.calls = lib.mkIf cfg.enabled {

View File

@ -1,8 +1,6 @@
{ ... }:
{
sane.programs.celeste64 = {
buildCost = 1;
sandbox.method = "bwrap";
sandbox.whitelistAudio = true;
sandbox.whitelistDri = true;

View File

@ -1,4 +1,4 @@
{ pkgs, ... }:
{ config, pkgs, ... }:
{
sane.programs.conky = {
# TODO: non-sandboxed `conky` still ships via `sxmo-utils`, but unused
@ -14,7 +14,6 @@
fs.".config/conky/conky.conf".symlink.target =
let
# TODO: make this just another `suggestedPrograms`!
battery_estimate = pkgs.static-nix-shell.mkBash {
pname = "battery_estimate";
srcRoot = ./.;
@ -27,8 +26,14 @@
services.conky = {
description = "conky dynamic desktop background";
partOf = [ "graphical-session" ];
command = "conky";
after = [ "graphical-session.target" ];
# partOf = [ "graphical-session.target" ]; # propagate stop/restart signal from graphical-session to this unit
wantedBy = [ "graphical-session.target" ];
serviceConfig.ExecStart = "${config.sane.programs.conky.package}/bin/conky";
serviceConfig.Type = "simple";
serviceConfig.Restart = "on-failure";
serviceConfig.RestartSec = "10s";
};
};
}

View File

@ -13,8 +13,6 @@
'';
});
buildCost = 1;
sandbox.method = "bwrap"; # landlock gives: _multiprocessing.SemLock: Permission Denied
sandbox.whitelistAudio = true;
sandbox.whitelistDbus = [ "user" ]; # mpris

View File

@ -1,34 +0,0 @@
# docs: <https://wiki.nixos.org/wiki/Printing>
# to add a printer:
# 1. <http://localhost:631/admin/>
# 2. click "find new printers" and follow prompts
# - prefer to use the "Generic IPP Everywhere Printer" driver
# alternatively, add/modify printers by running
# - `system-config-printer`
{ config, lib, ... }:
let
cfg = config.sane.programs.cups;
in
{
sane.programs.cups = {
suggestedPrograms = [
"system-config-printer"
];
};
sane.programs.system-config-printer = {};
services.printing = lib.mkIf cfg.enabled {
enable = true;
startWhenNeeded = false; #< a.k.a. socket activated?
# webInterface = false;
# logLevel = "debug"; # default: "info"
# extraConfig = "<lines ... >";
# drivers = [ <cups driver packages...> ]
};
# services.avahi = lib.mkIf cfg.enabled {
# # only needed for wireless printing
# enable = true;
# nssmdns4 = true;
# openFirewall = true;
# };
}

View File

@ -1,35 +0,0 @@
{ 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
'';
});
# TODO: try to sandbox this better? maybe i can have fuse (unsandboxed) invoke curlftpfs (sandboxed)?
# - landlock gives EPERM
# - bwrap just silently doesn't mount it, maybe because of setuid stuff around fuse?
# sandbox.method = "capshonly";
# sandbox.net = "all";
# sandbox.capabilities = [
# "sys_admin"
# "sys_module"
# ];
};
}

View File

@ -34,8 +34,15 @@ in
services.dconf = {
description = "dconf configuration database/server";
partOf = [ "graphical-session" ];
command = "${lib.getLib cfg.package}/libexec/dconf-service";
after = [ "graphical-session.target" ];
wantedBy = [ "graphical-session.target" ];
serviceConfig = {
ExecStart = "${lib.getLib cfg.package}/libexec/dconf-service";
Type = "simple";
Restart = "always";
RestartSec = "5s";
};
};
# supposedly necessary for packages which haven't been wrapped (i.e. wrapGtkApp?),

View File

@ -21,17 +21,12 @@
./chatty.nix
./conky
./cozy.nix
./cups.nix
./curlftpfs.nix
./dconf.nix
./deadd-notification-center
./dialect.nix
./dino.nix
./dissent.nix
./dtrx.nix
./eg25-control.nix
./element-desktop.nix
./engrampa.nix
./epiphany.nix
./evince.nix
./fcitx5.nix
@ -48,7 +43,6 @@
./gdbus.nix
./geary.nix
./git.nix
./gnome-clocks.nix
./gnome-feeds.nix
./gnome-keyring
./gnome-maps.nix
@ -57,17 +51,14 @@
./gpodder.nix
./grimshot.nix
./gthumb.nix
./gvfs.nix
./handbrake.nix
./helix.nix
./htop
./imagemagick.nix
./jellyfin-media-player.nix
./kdenlive.nix
./komikku.nix
./koreader
./less.nix
./lftp.nix
./libreoffice.nix
./lemoa.nix
./loupe.nix
@ -75,20 +66,17 @@
./megapixels.nix
./mepo.nix
./mimeo
./modemmanager.nix
./mopidy.nix
./mpv
./msmtp.nix
./nautilus.nix
./neovim.nix
./networkmanager.nix
./newsflash.nix
./nheko.nix
./nicotine-plus.nix
./nix-index.nix
./notejot.nix
./ntfy-sh.nix
./objdump.nix
./obsidian.nix
./offlineimap.nix
./open-in-mpv.nix
@ -99,13 +87,9 @@
./rhythmbox.nix
./ripgrep.nix
./rofi
./rtkit.nix
./s6-rc.nix
./sane-input-handler
./sane-open.nix
./sane-screenshot.nix
./sane-scripts.nix
./sanebox.nix
./schlock.nix
./sfeed.nix
./signal-desktop.nix
@ -134,7 +118,7 @@
./wine.nix
./wireplumber.nix
./wireshark.nix
./wpa_supplicant.nix
./wob
./wvkbd.nix
./xarchiver.nix
./xdg-desktop-portal.nix
@ -145,7 +129,6 @@
./zathura.nix
./zeal.nix
./zecwallet-lite.nix
./zulip.nix
./zsh
];

View File

@ -1,6 +1,15 @@
{ pkgs, ... }:
{
sane.programs.dialect = {
sandbox.method = "bwrap";
sandbox.wrapperType = "inplace"; # share/search_providers/ calls back into the binary, weird wrap semantics
sandbox.whitelistWayland = true;
sandbox.net = "clearnet";
sandbox.extraHomePaths = [
".config/dconf" # won't start without it
];
suggestedPrograms = [ "dconf" ]; #< to persist settings
packageUnwrapped = pkgs.dialect.overrideAttrs (upstream: {
# TODO: send upstream
# TODO: figure out how to get audio working
@ -9,17 +18,5 @@
pkgs.glib-networking # for TLS
];
});
suggestedPrograms = [ "dconf" ]; #< to persist settings
buildCost = 1;
sandbox.method = "bwrap";
sandbox.wrapperType = "inplace"; # share/search_providers/ calls back into the binary, weird wrap semantics
sandbox.whitelistWayland = true;
sandbox.net = "clearnet";
sandbox.extraHomePaths = [
".config/dconf" # won't start without it
];
};
}

View File

@ -14,27 +14,22 @@
# but at present it has no "start in tray" type of option: it must render a window.
#
# 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
# - fix is to toggle it off -> on in the Dino UI
# - default mic gain is WAY TOO MUCH (heavily distorted)
# - on lappy/desktop, right-clicking the mic button allows to toggle audio devices, but impossible to trigger this on moby/touch screen!
# - TODO: dino should have more optimal niceness/priority to ensure it can process its buffers
# - possibly this is solved by enabling RealtimeKit (rtkit)
# - TODO: see if Dino calls work better with `echo full > /sys/kernel/debug/sched/preempt`
#
# probably fixed:
# - once per 1-2 minutes dino will temporarily drop mic input:
# - `rtp-WARNING: plugin.vala:148: Warning in pipeline: Can't record audio fast enough
# - `rtp-WRNING: plugin.vala:148: Warning in pipeline: Can't record audio fast enough
# - this was *partially* fixed by bumping the pipewire mic buffer to 2048 samples (from ~512)
# - this was further fixed by setting PULSE_LATENCY_MSEC=20.
# - possibly Dino should be updated internally: `info.rate / 100` -> `info.rate / 50`.
# - i think that affects the batching for echo cancellation, adaptive gain control, etc.
# - dino *should* be able to use Pipewire directly for calls instead of going through pulse, but had trouble achieving that in actuality
#
{ config, lib, pkgs, ... }:
{ config, lib, ... }:
let
cfg = config.sane.programs.dino;
in
@ -50,33 +45,6 @@ in
};
};
packageUnwrapped = (pkgs.dino.override {
# XXX(2024/04/24): build without echo cancelation (i.e. force WITH_VOICE_PROCESSOR to be undefined).
# this means that if the other end of the call is on speaker phone, i'm liable to hear my own voice
# leave their speaker, enter their mic, and then return to me.
# the benefit is a >50% reduction in CPU use. insignificant on any modern PC; make-or-break on a low-power Pinephone.
webrtc-audio-processing = null;
}).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-28";
src = lib.warnIf (lib.versionOlder "0.4.3" upstream.version) "dino update: safe to remove sane patches" pkgs.fetchFromGitHub {
owner = "dino";
repo = "dino";
rev = "657502955567dd538e56f300e075c7db52e25d74";
hash = "sha256-SApJy9FgxxLOB5A/zGtpdFZtSqSiS03vggRrCte1tFE=";
};
# avoid double-application of upstreamed patches
# https://github.com/NixOS/nixpkgs/pull/309265
patches = [];
checkPhase = ''
runHook preCheck
./xmpp-vala-test
# ./signal-protocol-vala-test # doesn't exist anymore
runHook postCheck
'';
});
sandbox.method = "bwrap";
sandbox.net = "clearnet";
sandbox.whitelistAudio = true;
@ -100,34 +68,26 @@ in
services.dino = {
description = "dino XMPP client";
partOf = lib.mkIf cfg.config.autostart [ "graphical-session" ];
after = [ "graphical-session.target" ];
# 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>
# dino defaults to 10ms mic buffer, which causes underruns, which Dino handles *very* poorly
# as in, the other end of the call will just not receive sound from us for a couple seconds.
# pipewire uses power-of-two buffering for the mic itself (by default), but this env var supports only whole numbers, which isn't quite reconcilable:
# - 1024/48000 = 21.33ms
# - 2048/48000 = 42.67ms
# - 4096/48000 = 85.33ms
# also, Dino's likely still doing things in 10ms batches internally.
#
# note that this number supposedly is just the buffer size which Dino asks Pulse (pipewire) to share with it.
# in theory, it's equivalent to adjusting pipewire's quanta setting, and so isn't additive to the existing pipewire buffers.
# (and would also be overriden by pipewire's quanta.min setting).
# but in practice, setting this seems to have some more effect beyond just the buffer sizes visible in `pw-top`.
#
# 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 (and seems to have very little impact in practice anyway).
# buffer size:
# - 1024 (PULSE_LATENCY_MSEC=20): `pw-top` shows several underruns per second.
# - 2048 (PULSE_LATENCY_MSEC=50): `pw-top` shows very few underruns: maybe 1-5 per minute. with voice processor disabled, this works well. with it enabled, i still get gaps in which the mic "disappears".
# - 4096 (PULSE_LATENCY_MSEC=100): `pw-top` shows 0 underruns. with voice processor disabled, i seem to be permanently muted. with it enabled, this works well.
#
# pipewire uses power-of-two buffering for the mic itself. that would put us at 21.33 ms, but this env var supports only whole numbers (21ms ends up not power-of-two).
# also, Dino's likely still doing things in 10ms batches internally anyway.
environment.PULSE_LATENCY_MSEC = "20";
# note that debug logging during calls produces so much journal spam that it pegs the CPU and causes dropped audio
# env G_MESSAGES_DEBUG = "all";
command = "env PULSE_LATENCY_MSEC=50 nice -n -15 dino";
# environment.G_MESSAGES_DEBUG = "all";
};
};
}

View File

@ -28,7 +28,7 @@ in
# - <https://github.com/diamondburned/dissent/issues/139>
# - <https://github.com/zalando/go-keyring/issues/46>
substituteInPlace vendor/github.com/zalando/go-keyring/secret_service/secret_service.go \
--replace-fail '"login"' '"Default_keyring"'
--replace '"login"' '"Default_keyring"'
'';
});
sandbox.method = "bwrap";
@ -57,8 +57,15 @@ in
services.dissent = {
description = "dissent Discord client";
partOf = lib.mkIf cfg.config.autostart [ "graphical-session" ];
command = "dissent";
after = [ "graphical-session.target" ];
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/dissent";
Type = "simple";
Restart = "always";
RestartSec = "20s";
};
};
};
}

View File

@ -1,16 +0,0 @@
{ pkgs, ... }:
{
sane.programs.dtrx = {
packageUnwrapped = pkgs.dtrx.override {
# `binutils` is the nix wrapper, which reads nix-related env vars
# before passing on to e.g. `ld`.
# dtrx probably only needs `ar` at runtime, not even `ld`.
binutils = pkgs.binutils-unwrapped;
# build without rpm support, since `rpm` package doesn't cross-compile.
rpm = null;
};
sandbox.method = "bwrap";
sandbox.whitelistPwd = true;
sandbox.autodetectCliPaths = "existing"; #< for the archive
};
}

View File

@ -1,74 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.eg25-control;
in
{
sane.programs.eg25-control = {
suggestedPrograms = [ "modemmanager" ];
services.eg25-control-powered = {
description = "eg25-control-powered: power to the Qualcomm eg25 modem used by PinePhone";
startCommand = "eg25-control --power-on --verbose";
cleanupCommand = "eg25-control --power-off --verbose";
# depends = [ "ModemManager" ]
};
services.eg25-control-gps = {
# TODO: separate almanac upload from GPS enablement
# - don't want to re-upload the almanac everytime the GPS is toggled
# - want to upload almanac even when GPS *isn't* enabled, if we have internet connection.
description = "eg25-control-gps: background GPS tracking";
startCommand = "eg25-control --enable-gps --dump-debug-info --verbose";
cleanupCommand = "eg25-control --disable-gps --dump-debug-info --verbose";
depends = [ "eg25-control-powered" ];
};
};
# TODO: port to s6
systemd.services.eg25-control-freshen-agps = lib.mkIf cfg.enabled {
description = "keep assisted-GPS data fresh";
serviceConfig = {
# XXX: this can have a race condition with eg25-control-gps
# - eg25-control-gps initiates DL of new/<agps>
# - eg25-control-gps tests new/<agps>: it works
# - eg25-control-freshen-agps initiates DL of new/<agps>
# - eg25-control-gps: moves new/<agps> into cache/
# - but it moved the result (possibly incomplete) of eg25-control-freshen-agps, incorrectly
# in practice, i don't expect much issue from this.
ExecStart = "${cfg.package}/bin/eg25-control --ensure-agps-cache --verbose";
Restart = "no";
User = "eg25-control";
WorkingDirectory = "/var/lib/eg25-control";
StateDirectory = "eg25-control";
};
startAt = "hourly"; # this is a bit more than necessary, but idk systemd calendar syntax
after = [ "network-online.target" "nss-lookup.target" ];
requires = [ "network-online.target" ];
# wantedBy = [ "network-online.target" ]; # auto-start immediately after boot
};
users = lib.mkIf cfg.enabled {
groups.eg25-control = {};
users.eg25-control = {
group = "eg25-control";
isSystemUser = true;
home = "/var/lib/eg25-control";
extraGroups = [
"dialout" # required to read /dev/ttyUSB1
"networkmanager" # required to authenticate with mmcli
];
};
};
sane.persist.sys.byStore.plaintext = lib.mkIf cfg.enabled [
# to persist agps data, i think.
{ user = "eg25-control"; group = "eg25-control"; path = "/var/lib/eg25-control"; }
];
services.udev.extraRules = let
chmod = "${pkgs.coreutils}/bin/chmod";
chown = "${pkgs.coreutils}/bin/chown";
in ''
# make Modem controllable by user
DRIVER=="modem-power", RUN+="${chmod} g+w /sys%p/powered", RUN+="${chown} :networkmanager /sys%p/powered"
'';
}

View File

@ -4,29 +4,18 @@
# - <https://github.com/vector-im/element-desktop/issues/1029#issuecomment-1632688224>
# - `rm -rf ~/.config/Element/GPUCache`
# - <https://github.com/NixOS/nixpkgs/issues/244486>
{ lib, pkgs, ... }:
{ pkgs, ... }:
{
sane.programs.element-desktop = {
packageUnwrapped = (pkgs.element-desktop.override {
# use pre-built electron because otherwise it takes 4 hrs to build from source.
packageUnwrapped = pkgs.element-desktop.override {
# use pre-build electron because otherwise it takes 4 hrs to build from source.
electron = pkgs.electron-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 = [
"gnome-keyring"
"xwayland"
];
buildCost = 1;
sandbox.method = "bwrap";
sandbox.net = "clearnet";
sandbox.whitelistAudio = true;

View File

@ -1,17 +0,0 @@
{ 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"
];
};
}

View File

@ -23,8 +23,6 @@
"tmp"
];
buildCost = 2;
# XXX(2023/07/08): running on moby without `WEBKIT_DISABLE_SANDBOX...` fails, with:
# - `bwrap: Can't make symlink at /var/run: File exists`
# this could be due to:

View File

@ -1,8 +1,6 @@
{ ... }:
{
sane.programs.evince = {
buildCost = 1;
sandbox.method = "bwrap";
sandbox.autodetectCliPaths = true;
sandbox.whitelistWayland = true;

View File

@ -24,7 +24,10 @@
# - nixpkgs has a few themes: `fcitx5-{material-color,nord,rose-pine}`
# - NUR has a few themes
# - <https://github.com/catppuccin/fcitx5>
{ lib, pkgs, ... }:
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.fcitx5;
in
{
sane.programs.fcitx5 = {
packageUnwrapped = pkgs.fcitx5-with-addons.override {
@ -97,8 +100,15 @@
services.fcitx5 = {
description = "fcitx5: input method (IME) for emoji/internationalization";
partOf = [ "graphical-session" ];
command = "fcitx5";
after = [ "graphical-session.target" ];
wantedBy = [ "graphical-session.target" ];
serviceConfig = {
ExecStart="${cfg.package}/bin/fcitx5";
Type = "simple";
Restart = "always";
RestartSec = "10s";
};
};
env.XMODIFIERS = "@im=fcitx";

View File

@ -96,16 +96,18 @@ in
services.feedbackd = {
description = "feedbackd audio/vibration/led controller";
depends = [ "sound" ];
partOf = [ "default" ];
command = lib.concatStringsSep " " ([
"env"
"G_MESSAGES_DEBUG=all"
] ++ lib.optionals cfg.config.proxied [
"FEEDBACK_THEME=$HOME/.config/feedbackd/themes/proxied.json"
] ++ [
"${cfg.package}/libexec/feedbackd"
]);
wantedBy = [ "default.target" ]; #< should technically be `sound.target`, but that doesn't seem to get auto-started?
serviceConfig = {
ExecStart = "${cfg.package}/libexec/feedbackd";
Type = "simple";
Restart = "on-failure";
RestartSec = "10s";
};
environment = {
G_MESSAGES_DEBUG = "all";
} // (lib.optionalAttrs cfg.config.proxied {
FEEDBACK_THEME = "/home/colin/.config/feedbackd/themes/proxied.json";
});
};
};

View File

@ -207,10 +207,6 @@ in
package = pkgs.firefox-extensions.i2p-in-private-browsing;
enable = lib.mkDefault config.services.i2p.enable;
};
i-still-dont-care-about-cookies = {
package = pkgs.firefox-extensions.i-still-dont-care-about-cookies;
enable = lib.mkDefault true;
};
open-in-mpv = {
# test: `open-in-mpv 'mpv:///open?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ'`
package = pkgs.firefox-extensions.open-in-mpv;
@ -314,8 +310,8 @@ in
// scrollbar configuration, see: <https://artemis.sh/2023/10/12/scrollbars.html>
// style=4 gives rectangular scrollbars
// could also enable "always show scrollbars" in about:preferences -- not sure what the actual pref name for that is
// note that too-large scrollbars (like 50px wide, even 20px) tend to obscure content (and make buttons unclickable)
defaultPref("widget.non-native-theme.scrollbar.size.override", 14);
// note that too-large scrollbars (like 50px wide) tend to obscure content (and make buttons unclickable)
defaultPref("widget.non-native-theme.scrollbar.size.override", 20);
defaultPref("widget.non-native-theme.scrollbar.style", 4);
// disable inertial/kinetic/momentum scrolling because it just gets in the way on touchpads

View File

@ -30,30 +30,6 @@ let
nerdfontPkgs = builtins.map
(f: pkgs.nerdfonts.override { fonts = [ f ]; })
wantedNerdfonts;
# see: <repo:nixos/nixpkgs:nixos/modules/config/fonts/fontconfig.nix>
# and: <repo:nixos/nixpkgs:pkgs/development/libraries/fontconfig/make-fonts-cache.nix>
# nixpkgs creates a fontconfig cache, but only when *not* cross compiling.
# but the alternative is that fonts are cached purely at runtime, in ~/.cache/fontconfig,
# and that needs to either be added to the sandbox of *every* app,
# or font-heavy apps are several *seconds* slower to launch.
#
# TODO: upstream this into `make-fonts-cache.nix`?
cache = (pkgs.makeFontsCache { fontDirectories = config.fonts.packages; }).overrideAttrs (upstream: {
buildCommand = lib.replaceStrings
[ "fc-cache" ]
[ "${pkgs.stdenv.hostPlatform.emulator pkgs.buildPackages} ${pkgs.fontconfig.bin}/bin/fc-cache" ]
upstream.buildCommand
;
});
cacheConf = pkgs.writeTextDir "etc/fonts/conf.d/01-nixos-cache-cross.conf" ''
<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'urn:fontconfig:fonts.dtd'>
<fontconfig>
<!-- Pre-generated font caches -->
<cachedir>${cache}</cachedir>
</fontconfig>
'';
in
{
sane.programs.fontconfig = {
@ -88,8 +64,6 @@ in
"DejaVu Sans"
];
};
# nixpkgs builds a cache file, but only for non-cross. i want it always, so add my own cache -- but ONLY for cross.
fontconfig.confPackages = lib.mkIf (pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform) [ cacheConf ];
#vvv enables dejavu_fonts, freefont_ttf, gyre-fonts, liberation_ttf, unifont, noto-fonts-emoji
enableDefaultPackages = false;
packages = with pkgs; [

View File

@ -68,10 +68,17 @@ in
services.fractal = {
description = "fractal Matrix client";
partOf = lib.mkIf cfg.config.autostart [ "graphical-session" ];
after = [ "graphical-session.target" ];
# partOf = [ "graphical-session.target" ];
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
# env "G_MESSAGES_DEBUG=all"
command = "fractal";
serviceConfig = {
ExecStart = "${cfg.package}/bin/fractal";
Type = "simple";
Restart = "always";
RestartSec = "20s";
};
# environment.G_MESSAGES_DEBUG = "all";
};
};
}

View File

@ -2,6 +2,11 @@
{ pkgs, ... }:
{
sane.programs.frozen-bubble = {
sandbox.method = "bwrap";
sandbox.net = "clearnet"; # net play
sandbox.whitelistAudio = true;
sandbox.whitelistWayland = true;
packageUnwrapped = pkgs.frozen-bubble.overrideAttrs (upstream: {
# patch so it stores its dot-files not in root ~.
postPatch = (upstream.postPatch or "") + ''
@ -9,12 +14,6 @@
--replace-fail '$FBHOME = "$ENV{HOME}/.frozen-bubble"' '$FBHOME = "$ENV{HOME}/.local/share/frozen-bubble"'
'';
});
buildCost = 1;
sandbox.method = "bwrap";
sandbox.net = "clearnet"; # net play
sandbox.whitelistAudio = true;
sandbox.whitelistWayland = true;
persist.byStore.plaintext = [
".local/share/frozen-bubble" # preferences, high scores

View File

@ -8,8 +8,6 @@
{ ... }:
{
sane.programs.g4music = {
buildCost = 1;
sandbox.method = "bwrap";
sandbox.whitelistAudio = true;
sandbox.whitelistDbus = [ "user" ]; # mpris

View File

@ -37,7 +37,7 @@ in
# fs.".config/geary".dir = {};
# fs.".local/share/folks".dir = {};
buildCost = 3; # uses webkitgtk 4.1
slowToBuild = true; # uses webkitgtk 4.1
persist.byStore.private = [
# attachments, and email -- contained in a sqlite db
".local/share/geary"
@ -87,8 +87,16 @@ in
services.geary = {
description = "geary email client";
partOf = lib.mkIf cfg.config.autostart [ "graphical-session" ];
command = "geary";
after = [ "graphical-session.target" ];
# partOf = [ "graphical-session.target" ];
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/geary";
Type = "simple";
Restart = "always";
RestartSec = "20s";
};
};
};

View File

@ -1,24 +0,0 @@
{ pkgs, ... }: {
sane.programs."gnome.gnome-clocks" = {
packageUnwrapped = pkgs.gnome.gnome-clocks.overrideAttrs (upstream: {
# TODO: upstream this
buildInputs = upstream.buildInputs ++ (with pkgs; [
# gnome-clocks needs `playbin` (gst-plugins-base) and `scaletempo` (gst-plugins-good)
# to play the alarm when a timer expires
gst_all_1.gstreamer
gst_all_1.gst-plugins-base
gst_all_1.gst-plugins-good
]);
});
buildCost = 1;
sandbox.method = "bwrap";
sandbox.whitelistAudio = true;
sandbox.whitelistDbus = [ "user" ]; #< required (alongside .config/dconf) to remember timers
sandbox.whitelistWayland = true;
sandbox.extraPaths = [
".config/dconf" # required (alongside dbus) to remember timers
];
suggestedPrograms = [ "dconf" ];
};
}

View File

@ -1,4 +1,7 @@
{ lib, pkgs, ... }:
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.gnome-keyring;
in
{
sane.programs.gnome-keyring = {
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-keyring;
@ -26,9 +29,9 @@
fs.".local/share/keyrings/default" = {
file.text = "Default_keyring.keyring"; #< no trailing newline
# wantedBy = [ config.sane.fs."${config.sane.persist.stores.private.origin}".unit ];
# wantedBeforeBy = [ #< don't create this as part of `multi-user.target`
# "gnome-keyring.service" # TODO: sane.programs should declare this dependency for us
# ];
wantedBeforeBy = [ #< don't create this as part of `multi-user.target`
"gnome-keyring.service" # TODO: sane.programs should declare this dependency for us
];
};
# 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.
@ -41,20 +44,22 @@
lock-after=false
'';
# wantedBy = [ config.sane.fs."${config.sane.persist.stores.private.origin}".unit ];
# wantedBeforeBy = [ #< don't create this as part of `multi-user.target`
# "gnome-keyring.service"
# ];
wantedBeforeBy = [ #< don't create this as part of `multi-user.target`
"gnome-keyring.service"
];
};
services.gnome-keyring = {
description = "gnome-keyring-daemon: secret provider";
partOf = [ "graphical-session" ];
command = let
gkr-start = pkgs.writeShellScriptBin "gnome-keyring-daemon-start" ''
mkdir -m 0700 -p $XDG_RUNTIME_DIR/keyring
exec gnome-keyring-daemon --start --foreground --components=secrets
'';
in "${gkr-start}/bin/gnome-keyring-daemon-start";
after = [ "graphical-session.target" ];
wantedBy = [ "graphical-session.target" ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/gnome-keyring-daemon --start --foreground --components=secrets";
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -m 0700 -p %t/keyring";
Type = "simple";
Restart = "always";
RestartSec = "20s";
};
};
};
}

View File

@ -3,8 +3,6 @@
{ ... }:
{
sane.programs."gnome.gnome-weather" = {
buildCost = 1;
sandbox.method = "bwrap";
sandbox.wrapperType = "inplace"; #< /share/org.gnome.Weather/org.gnome.Weather file refers to bins by full path
sandbox.whitelistWayland = true;

View File

@ -2,20 +2,8 @@
# - turn the tv off and on again (no, really...)
#
# SANITY CHECKS:
# - `go2tv -u 'https://uninsane.org/share/Milkbags/AmenBreak.mp4'`
# - `go2tv -u 'https://uninsane.org/share/AmenBreak.mp4'`
# - LGTV: works, but not seekable
# - Samsung: "Cannot play video."
# - `go2tv -v /mnt/servo/media/Videos/Milkbags/AmenBreak.mp4`
# - Samsung: works
# - `go2tv -v /mnt/servo/media/Videos/Milkbags/COLIN.webm`
# - Samsung: works
# - `go2tv -v /mnt/servo/media/Videos/Shows/Lucky.Star/S01/S01E01-The.Girl.who.Dashes.Off.mkv`
# - Samsung: error 500
# - `go2tv -tc -v /mnt/servo/media/Videos/Shows/Lucky.Star/S01/S01E01-The.Girl.who.Dashes.Off.mkv`
# - Samsung: error 500
# - note that it still advertized .mkv to the TV
# - `cp /mnt/servo/media/Videos/Shows/Lucky.Star/S01/S01E01-The.Girl.who.Dashes.Off.mkv S01E01-The.Girl.who.Dashes.Off.mp4 && go2tv -v S01E01-The.Girl.who.Dashes.Off.mp4`
# - Samsung: WORKS
# - `go2tv -u 'https://youtu.be/p3G5IXn0K7A'`
# - LGTV: FAILS ("this file cannot be recognized")
# - no fix via transcoding, altering the URI, etc.
@ -29,8 +17,6 @@
# - LGTV: works
#
# WHEN TO TRANSCODE:
# - mkv container + *:
# - Samsung: rename to .mp4 and cast that: no need to transcode
# - mkv container + mpeg-2 video + AC-3/48k stereo audio:
# - LGTV: no transcoding needed
# - mkv container + H.264 video + AAC/48k 5.1 audio:
@ -41,7 +27,6 @@
# - LGTV: no transcoding needed
# - mkv container + H.265 video + E-AC-3/48k stereo audio:
# - LGTV: no transcoding needed
#
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.go2tv;

View File

@ -1,20 +0,0 @@
# gvfs is used by e.g. nautilus to mount remote filesystems (ftp://, etc)
# TODO: gvfs depends on udisks, depends on gnupg,
# and as part of this `keyboxd` gpg daemon gets started and does background work every minute even though i totally don't use it.
{ config, pkgs, ... }:
let
cfg = config.sane.programs.gvfs;
in
{
sane.programs.gvfs = {
packageUnwrapped = pkgs.gvfs.override {
# i don't need to mount samba shares, and samba build is expensive/flaky (mostly for cross, but even problematic on native)
samba = null;
};
};
services.gvfs = {
inherit (cfg) package;
enable = cfg.enabled;
};
}

View File

@ -1,8 +1,6 @@
{ pkgs, ... }:
{
sane.programs.handbrake = {
buildCost = 1;
sandbox.method = "landlock"; #< also supports bwrap, but landlock ensures we don't write to non-mounted tmpfs dir
sandbox.whitelistDbus = [ "user" ]; # notifications
sandbox.whitelistWayland = true;
@ -17,7 +15,7 @@
# disable expensive sambda dependency; i don't use it.
packageUnwrapped = pkgs.handbrake.override {
ffmpeg-full = pkgs.ffmpeg-full.override {
ffmpeg_5-full = pkgs.ffmpeg_5-full.override {
withSamba = false;
};
};

View File

@ -1,11 +0,0 @@
{ ... }:
{
sane.programs.htop = {
sandbox.method = "landlock";
sandbox.extraPaths = [
"/proc"
"/sys/devices"
];
fs.".config/htop/htoprc".symlink.target = ./htoprc;
};
}

View File

@ -1,63 +0,0 @@
# 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 PRIORITY 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

View File

@ -1,8 +1,6 @@
{ pkgs, ... }:
{
sane.programs.imagemagick = {
buildCost = 1;
sandbox.method = "bwrap";
sandbox.wrapperType = "inplace"; # /etc/ImageMagick-7/delegates.xml refers to bins by absolute path
sandbox.whitelistPwd = true;

View File

@ -1,15 +1,6 @@
{ pkgs, ... }:
{
sane.programs.kdenlive = {
packageUnwrapped = pkgs.kdenlive.override {
ffmpeg-full = pkgs.ffmpeg-full.override {
# avoid expensive samba build for a feature i don't use
withSamba = false;
};
};
buildCost = 1;
sandbox.method = "bwrap";
sandbox.extraHomePaths = [
"Music"
@ -23,5 +14,12 @@
sandbox.whitelistDbus = [ "user" ]; # notifications
sandbox.whitelistDri = true;
sandbox.whitelistWayland = true;
packageUnwrapped = pkgs.kdenlive.override {
ffmpeg-full = pkgs.ffmpeg-full.override {
# avoid expensive samba build for a feature i don't use
withSamba = false;
};
};
};
}

View File

@ -16,8 +16,6 @@
sandbox.whitelistDri = true; #< required
sandbox.whitelistWayland = true;
buildCost = 2;
secrets.".local/share/komikku/keyrings/plaintext.keyring" = ../../../secrets/common/komikku_accounts.json.bin;
# downloads end up here, and without the toplevel database komikku doesn't know they exist.
persist.byStore.plaintext = [

View File

@ -47,7 +47,6 @@ in {
packageUnwrapped = pkgs.koreader-from-src;
sandbox.method = "bwrap"; # sandboxes fine under landlock too, except for FTP
sandbox.net = "clearnet";
sandbox.whitelistDbus = [ "user" ]; # for opening the web browser via portal
sandbox.whitelistDri = true; # reduces startup time and subjective page flip time
sandbox.whitelistWayland = true;
sandbox.extraHomePaths = [

View File

@ -1,7 +1,6 @@
{ ... }:
{
sane.programs.lemoa = {
buildCost = 1;
sandbox.method = "bwrap";
sandbox.net = "clearnet";
sandbox.whitelistDbus = [ "user" ]; # for clicking links

View File

@ -1,13 +0,0 @@
{ ... }:
{
sane.programs.lftp = {
sandbox.method = "bwrap";
sandbox.net = "all";
sandbox.extraPaths = [
"Music"
"Videos/local"
"Videos/servo"
"tmp"
];
};
}

View File

@ -16,7 +16,7 @@
"tmp"
];
buildCost = 3;
slowToBuild = true;
# disable first-run stuff
fs.".config/libreoffice/4/user/registrymodifications.xcu".symlink.text = ''

View File

@ -30,7 +30,6 @@
];
mime.associations = {
"image/avif" = "org.gnome.Loupe.desktop";
"image/gif" = "org.gnome.Loupe.desktop";
"image/heif" = "org.gnome.Loupe.desktop"; # apple codec
"image/png" = "org.gnome.Loupe.desktop";

View File

@ -53,8 +53,13 @@
# on environment.packages, but then logs are blackholed.
services.mako = {
description = "mako desktop notification daemon";
partOf = [ "graphical-session" ];
command = "${config.sane.programs.mako.package}/bin/mako";
wantedBy = [ "graphical-session.target" ];
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";
};
};
}

View File

@ -1,16 +1,6 @@
{ pkgs, ... }:
{ ... }:
{
sane.programs.megapixels = {
packageUnwrapped = pkgs.megapixels.overrideAttrs (upstream: {
postPatch = (upstream.postPatch or "") + ''
# 2024/04/21: patch it to save photos in a more specific directory
substituteInPlace src/process_pipeline.c \
--replace-fail 'XDG_PICTURES_DIR' 'XDG_PHOTOS_DIR'
# 2024/04/21: patch it so the folder button works
substituteInPlace src/main.c \
--replace-fail 'g_get_user_special_dir(G_USER_DIRECTORY_PICTURES)' 'getenv("XDG_PHOTOS_DIR")'
'';
});
# megapixels sandboxing is tough:
# if misconfigured, preview will alternately be OK, black, or only 1/4 of it will be rendered -- with no obvious pattern.
# adding all of ~ to the sandbox will sometimes (?) fix the flakiness, even when `strace` doesn't show it accessing any files...
@ -26,8 +16,10 @@
sandbox.extraHomePaths = [
".config/dconf" #< else it segfaults during post-process
# ".config/megapixels"
".local/share/applications" #< needed for viewing photos, until i can sort out the portal stuff
".cache/mesa_shader_cache" # loads way faster
"Pictures/Photos"
"tmp"
"Pictures" #< TODO: make this Pictures/Photos and save photos there
# also it addresses a lot via relative path.
];
sandbox.extraPaths = [
@ -45,12 +37,6 @@
sandbox.extraRuntimePaths = [
"dconf" #< else it's very spammy, and slow
];
sandbox.extraConfig = [
# XXX(2024/04/21): without this it fails to convert .dng -> .jpg.
# "bwrap: open /proc/34/ns/ns failed: No such file or directory"
"--sanebox-keep-namespace" "pid"
];
suggestedPrograms = [ "dconf" ]; #< not sure if necessary
};
}

View File

@ -57,7 +57,6 @@ in
desktopName = "Mimeo";
exec = "mimeo %U";
comment = "Open files by MIME-type or file name using regular expressions.";
noDisplay = true;
})
];

View File

@ -1,38 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.modemmanager;
in
{
sane.programs.modemmanager = {
# mmcli needs /run/current-system/sw/share/dbus-1 files to function
enableFor.system = lib.mkIf (builtins.any (en: en) (builtins.attrValues cfg.enableFor.user)) true;
};
systemd.services.ModemManager = lib.mkIf cfg.enabled {
aliases = [ "dbus-org.freedesktop.ModemManager1.service" ];
after = [ "polkit.service" ];
requires = [ "polkit.service" ];
wantedBy = [ "network.target" ];
serviceConfig = {
Type = "dbus";
BusName = "org.freedesktop.ModemManager1";
# only if started with `--debug` does mmcli let us issue AT commands like
# `mmcli --modem any --command=<AT_CMD>`
ExecStart = "${cfg.package}/bin/ModemManager --debug";
# --debug sets DEBUG level logging: so reset
ExecStartPost = "${cfg.package}/bin/mmcli --set-logging=INFO";
Restart = "on-abort";
StandardError = "null";
CapabilityBoundingSet = "CAP_SYS_ADMIN CAP_NET_ADMIN";
ProtectSystem = true;
ProtectHome = true;
PrivateTmp = true;
RestrictAddressFamilies = "AF_NETLINK AF_UNIX AF_QIPCRTR";
NoNewPrivileges = true;
};
};
# so that ModemManager can discover when the modem appears
services.udev.packages = lib.mkIf cfg.enabled [ cfg.package ];
}

View File

@ -9,18 +9,7 @@
# - 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>
# - act as a DLNS renderer (sink): <https://github.com/xfangfang/Macast>
# debugging:
# - enter console by pressing backtick.
# > `set volume 50` -> sets application volume to 50%
@ -28,7 +17,6 @@
# > `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
#
@ -40,152 +28,90 @@
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=";
# };
# patch so that the volume control corresponds to `ao-volume`, i.e. the system-wide volume.
# this is particularly nice for moby, because it avoids the awkwardness that system volume
# is hard to adjust while screen is on.
# note that only under alsa (`-ao=alsa`) does `ao-volume` actually correspond to system volume.
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'" \
--replace-fail \
"mp.observe_property('mute'" \
"mp.observe_property('user-data/sane_sysvol/mute'"
--replace-fail "mp.observe_property('volume'" "mp.observe_property('ao-volume'"
substituteInPlace src/uosc/elements/Volume.lua \
--replace-fail \
"mp.commandv('set', 'volume'" \
"mp.set_property_number('user-data/sane_sysvol/volume'" \
--replace-fail \
"mp.set_property_native('volume'" \
"mp.set_property_number('user-data/sane_sysvol/volume'" \
--replace-fail \
"mp.set_property_native('mute'" \
"mp.set_property_bool('user-data/sane_sysvol/mute'" \
--replace-fail \
"mp.commandv('cycle', 'mute')" \
"mp.set_property_bool('user-data/sane_sysvol/mute', not mp.get_property_bool('user-data/sane_sysvol/mute'))"
--replace-fail "mp.commandv('set', 'volume'" "mp.commandv('set', 'ao-volume'" \
--replace-fail "mp.set_property_native('volume'" "mp.set_property('ao-volume'"
# tweak the top-bar "maximize" button to actually act as a "fullscreen" button.
substituteInPlace src/uosc/elements/TopBar.lua \
--replace-fail \
'get_maximized_command,' \
'"cycle fullscreen",'
# `ao-volume` isn't actually an observable property.
# as of 2024/03/02, they *may* be working on that:
# - <https://github.com/mpv-player/mpv/pull/13604#issuecomment-1971665736>
# in the meantime, just query the volume every tick (i.e. frame).
# alternative is mpv's JSON IPC feature, where i could notify its socket whenever pipewire volume changes.
cat <<EOF >> src/uosc/main.lua
function update_ao_volume()
local vol = mp.get_property('ao-volume')
if vol ~= nil then
vol = tonumber(vol)
if vol ~= state.volume then
set_state('volume', vol)
request_render()
end
end
end
-- tick seems to occur on every redraw (even when volume is hidden).
-- in practice: for every new frame of the source, or whenever the cursor is moved.
mp.register_event('tick', update_ao_volume)
-- if paused and cursor isn't moving, then `tick` isn't called. fallback to a timer.
mp.add_periodic_timer(2, update_ao_volume)
-- invoke immediately to ensure state.volume is non-nil
update_ao_volume()
if state.volume == nil then
state.volume = 0
end
EOF
'';
});
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 rec {
# N.B.: populating `self` to `luajit` is necessary for the resulting `lua.withPackages` function to preserve my override.
# i use enable52Compat in order to get `table.unpack`.
# i think using `luajit` here instead of `lua` is optional, just i get better perf with it :)
lua = pkgs.luajit.override { enable52Compat = true; self = lua; };
})
{
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}"
# ];
};
packageUnwrapped = with pkgs; wrapMpv mpv-unwrapped {
scripts = [
mpvScripts.mpris
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"
"sane-cast"
"sane-die-with-parent"
"go2tv"
"xdg-terminal-exec"
];
@ -198,7 +124,7 @@ in
sandbox.whitelistWayland = true;
sandbox.extraHomePaths = [
".config/mpv" #< else mpris plugin crashes on launch
".local/share/applications" #< for xdg-terminal-exec (sane-cast)
".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"
@ -213,9 +139,7 @@ in
# 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/scripts/sane/main.lua".symlink.target = ./sane-main.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;

View File

@ -9,8 +9,6 @@
POWER ignore
VOLUME_UP ignore
VOLUME_DOWN ignore
# disable "double-click to toggle fullscreen", else that limits the rate at which i can seek
MBTN_LEFT_DBL ignore
# uosc menu
# text after the shebang is parsed by uosc to construct the menu and names
@ -33,5 +31,7 @@ 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/sane-cast #! Cast
ctrl+r script-binding sane/blast #! Audiocast
ctrl+t script-binding sane/go2tv-video #! Cast
# script-binding sane/go2tv-stream #! Cast (...) > Stream
# script-binding sane/go2tv-gui #! Cast (...) > GUI

View File

@ -10,9 +10,7 @@ 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
# actually, prefer just a subset of what's enabled by pseudo-gui, else logging breaks
force-window=yes
player-operation-mode=pseudo-gui
# use uosc instead (for On Screen Controls)
osc=no
@ -21,8 +19,8 @@ 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
# 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

View File

@ -0,0 +1,34 @@
function subprocess(in_terminal, args)
if in_terminal then
args = { "xdg-terminal-exec", table.unpack(args) }
end
mp.command_native({
name = "subprocess",
args = args,
detach = false,
capture_stdout = false,
capture_stderr = false,
-- capture_size=0,
passthrough_stdin = false,
playback_only = false,
})
end
function invoke_go2tv(in_terminal, args)
mp.commandv("set", "pause", "yes")
subprocess(in_terminal, { "go2tv", table.unpack(args) })
end
function invoke_go2tv_on_open_file(mode)
local path = mp.get_property("stream-open-filename");
return invoke_go2tv(true, { mode, path })
end
mp.add_key_binding(nil, "blast", function() subprocess(false, { "blast-to-default" }) end)
mp.add_key_binding(nil, 'go2tv-gui', function() invoke_go2tv(false, {}) end)
mp.add_key_binding(nil, 'go2tv-video', function() invoke_go2tv_on_open_file("-v") end)
mp.add_key_binding(nil, 'go2tv-stream', function() invoke_go2tv_on_open_file("-s") end)
-- uncomment for debugging:
-- if mpv fails to eval this script (e.g. syntax error), then it will fail to quit on launch
-- mp.command('quit')

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