Compare commits
1 Commits
master
...
extend-git
Author | SHA1 | Date | |
---|---|---|---|
|
05231c84e8 |
25
TODO.md
25
TODO.md
|
@ -6,15 +6,16 @@
|
|||
- `ip monitor` can detect those manual link state changes (NM-dispatcher it seems cannot)
|
||||
- or try dnsmasq?
|
||||
- trust-dns: can't recursively resolve api.mangadex.org
|
||||
- nor `m.wikipedia.org` (`dyna.wikipedia.org`)
|
||||
- and *sometimes* apple.com fails
|
||||
- sandbox: link cache means that if i update ~/.config/... files inline, sandboxed programs still see the old version
|
||||
- mpv: audiocast has mpv sending its output to the builtin speakers unless manually changed
|
||||
- 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
|
||||
- syshud (volume overlay): when casting with `blast`, syshud doesn't react to volume changes
|
||||
- sysvol (volume overlay): when casting with `blast`, sysvol doesn't react to volume changes
|
||||
- moby: kaslr is effectively disabled
|
||||
- `dmesg | grep "KASLR disabled due to lack of seed"`
|
||||
- fix by adding `kaslrseed` to uboot script before `booti`
|
||||
|
@ -23,13 +24,6 @@
|
|||
- moby: bpf is effectively disabled?
|
||||
- `dmesg | grep 'systemd[1]: bpf-lsm: Failed to load BPF object: No such process'`
|
||||
- `dmesg | grep 'hid_bpf: error while preloading HID BPF dispatcher: -22'`
|
||||
- `s6` is not re-entrant
|
||||
- so if the desktop crashes, the login process from `unl0kr` fails to re-launch the GUI
|
||||
- swaync brightness slider does not work
|
||||
- it reads brightness from /sys/class/backlight/....
|
||||
- but to *set* the brightness it assumes systemd logind is running
|
||||
<repo:ErikReider/SwayNotificationCenter:src/controlCenter/widgets/backlight/backlightUtil.vala>
|
||||
no reason i can't just write to that file, or exec brightnessctl (if i learn vala)
|
||||
|
||||
## REFACTORING:
|
||||
- add import checks to my Python nix-shell scripts
|
||||
|
@ -90,12 +84,12 @@
|
|||
|
||||
### 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
|
||||
- watch `forkstat`: it does way too much
|
||||
- cleanup waybar/nwg-panel so that it's not invoking playerctl every 2 seconds
|
||||
- nwg-panel: doesn't know that virtual-desktop 10/TV exists
|
||||
- cleanup waybar so that it's not invoking playerctl every 2 seconds
|
||||
- install apps:
|
||||
- compass viewer (moby)
|
||||
- display QR codes for WiFi endpoints: <https://linuxphoneapps.org/apps/noappid.wisperwind.wifi2qr/>
|
||||
- shopping list (not in nixpkgs): <https://linuxphoneapps.org/apps/ro.hume.cosmin.shoppinglist/>
|
||||
- offline Wikipedia (or, add to `wike`)
|
||||
|
@ -125,11 +119,13 @@
|
|||
- don't show MPRIS if no players detected
|
||||
- this is a problem of playerctld, i guess
|
||||
- add option to change audio output
|
||||
- fix colors (red alert) to match overall theme
|
||||
- moby: tune GPS
|
||||
- fix iio-sensor-proxy magnetometer scaling
|
||||
- run only geoclue, and not gpsd, to save power?
|
||||
- tune QGPS setting in eg25-control, for less jitter?
|
||||
- direct mepo to prefer gpsd, with fallback to geoclue, for better accuracy?
|
||||
- configure geoclue to do some smoothing?
|
||||
- manually do smoothing, as some layer between mepo and geoclue?
|
||||
- manually do smoothing, as some layer between mepo and geoclue/gpsd?
|
||||
- moby: port `freshen-agps` timer service to s6 (maybe i want some `s6-cron` or something)
|
||||
- moby: show battery state on ssh login
|
||||
- moby: improve gPodder launch time
|
||||
|
@ -156,7 +152,6 @@
|
|||
- email: fix so that local mail doesn't go to junk
|
||||
- git sendmail flow adds the DKIM signatures, but gets delivered locally w/o having the sig checked, so goes into Junk
|
||||
- could change junk filter from "no DKIM success" to explicit "DKIM failed"
|
||||
- add an auto-reply address (e.g. `reply-test@uninsane.org`) which reflects all incoming mail; use this (or a friend running this) for liveness checks
|
||||
|
||||
### perf
|
||||
- debug nixos-rebuild times
|
||||
|
|
65
default.nix
65
default.nix
|
@ -1,5 +1,62 @@
|
|||
{ ... }@args:
|
||||
# limited, non-flake interface to this repo.
|
||||
# this file exposes the same view into `pkgs` which the flake would see when evaluated.
|
||||
#
|
||||
# the primary purpose of this file is so i can run `updateScript`s which expect
|
||||
# the root to be `default.nix`
|
||||
{ }:
|
||||
let
|
||||
sane-nix-files = import ./pkgs/additional/sane-nix-files { };
|
||||
in
|
||||
import "${sane-nix-files}/impure.nix" args
|
||||
mkPkgs = args: (import ./pkgs/additional/nixpkgs args).extend
|
||||
(import ./overlays/all.nix);
|
||||
inherit (mkPkgs {}) lib;
|
||||
|
||||
evalHost = { name, system, branch ? "master", variant ? null }:
|
||||
let
|
||||
pkgs = mkPkgs { inherit system; variant = branch; };
|
||||
in pkgs.nixos (
|
||||
[
|
||||
(lib.optionalAttrs (variant == "light") {
|
||||
sane.maxBuildCost = 2;
|
||||
})
|
||||
(lib.optionalAttrs (variant == "min") {
|
||||
sane.maxBuildCost = 0;
|
||||
})
|
||||
(import ./hosts/instantiate.nix { hostName = name; })
|
||||
(import ./modules)
|
||||
pkgs.sops-nix.nixosModules.sops
|
||||
]
|
||||
);
|
||||
mkFlavoredHost = args: let
|
||||
host = evalHost args;
|
||||
# expose the toplevel nixos system as the toplevel attribute itself,
|
||||
# with nested aliases for other common build targets
|
||||
in host.config.system.build.toplevel.overrideAttrs (base: {
|
||||
passthru = (base.passthru or {}) // {
|
||||
img = host.config.system.build.img;
|
||||
pkgs = host.config.system.build.pkgs;
|
||||
programs = lib.mapAttrs (_: p: p.package) host.config.sane.programs;
|
||||
toplevel = host.config.system.build.toplevel; #< self
|
||||
};
|
||||
});
|
||||
mkHost = args: {
|
||||
"${args.name}" = mkFlavoredHost args;
|
||||
"${args.name}-next" = mkFlavoredHost args // { branch = "staging-next"; };
|
||||
"${args.name}-staging" = mkFlavoredHost args // { branch = "staging"; };
|
||||
"${args.name}-light" = mkFlavoredHost args // { variant = "light"; };
|
||||
"${args.name}-light-next" = mkFlavoredHost args // { variant = "light"; branch = "staging-next"; };
|
||||
"${args.name}-light-staging" = mkFlavoredHost args // { variant = "light"; branch = "staging"; };
|
||||
"${args.name}-min" = mkFlavoredHost args // { variant = "min"; };
|
||||
"${args.name}-min-next" = mkFlavoredHost args // { variant = "min"; branch = "staging-next"; };
|
||||
"${args.name}-min-staging" = mkFlavoredHost args // { variant = "min"; branch = "staging-staging"; };
|
||||
};
|
||||
|
||||
hosts = lib.foldl' (acc: host: acc // (mkHost host)) {} [
|
||||
{ name = "crappy"; system = "armv7l-linux"; }
|
||||
{ name = "desko"; system = "x86_64-linux"; }
|
||||
{ name = "lappy"; system = "x86_64-linux"; }
|
||||
{ name = "moby"; system = "aarch64-linux"; }
|
||||
{ name = "rescue"; system = "x86_64-linux"; }
|
||||
{ name = "servo"; system = "x86_64-linux"; }
|
||||
];
|
||||
in (mkPkgs {}) // {
|
||||
inherit hosts;
|
||||
}
|
||||
|
|
7
flake.lock
Normal file
7
flake.lock
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"nodes": {
|
||||
"root": {}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
sane.programs.calls.enableFor.user.colin = false;
|
||||
sane.programs.consoleMediaUtils.enableFor.user.colin = true;
|
||||
sane.programs.epiphany.enableFor.user.colin = true;
|
||||
sane.programs.geary.enableFor.user.colin = false;
|
||||
sane.programs."gnome.geary".enableFor.user.colin = false;
|
||||
# sane.programs.firefox.enableFor.user.colin = true;
|
||||
sane.programs.portfolio-filemanager.enableFor.user.colin = true;
|
||||
sane.programs.signal-desktop.enableFor.user.colin = false;
|
||||
|
@ -25,7 +25,6 @@
|
|||
sane.programs.dino.config.autostart = false;
|
||||
sane.programs.dissent.config.autostart = false;
|
||||
sane.programs.fractal.config.autostart = false;
|
||||
sane.programs.sway.config.mod = "Mod1"; #< alt key instead of Super
|
||||
|
||||
# sane.programs.guiApps.enableFor.user.colin = false;
|
||||
|
||||
|
|
|
@ -32,14 +32,9 @@
|
|||
sane.programs.iphoneUtils.enableFor.user.colin = true;
|
||||
sane.programs.steam.enableFor.user.colin = true;
|
||||
|
||||
sane.programs.geary.config.autostart = true;
|
||||
sane.programs."gnome.geary".config.autostart = true;
|
||||
sane.programs.signal-desktop.config.autostart = true;
|
||||
|
||||
sane.programs.nwg-panel.config = {
|
||||
battery = false;
|
||||
brightness = false;
|
||||
};
|
||||
|
||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
|
||||
# needed to use libimobiledevice/ifuse, for iphone sync
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
sane.programs.stepmania.enableFor.user.colin = true;
|
||||
sane.programs.sway.enableFor.user.colin = true;
|
||||
|
||||
sane.programs.geary.config.autostart = true;
|
||||
sane.programs."gnome.geary".config.autostart = true;
|
||||
sane.programs.signal-desktop.config.autostart = true;
|
||||
|
||||
sops.secrets.colin-passwd.neededForUsers = true;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
{
|
||||
imports = [
|
||||
./fs.nix
|
||||
./gps.nix
|
||||
];
|
||||
|
||||
sane.hal.pine64.enable = true;
|
||||
|
@ -28,7 +29,6 @@
|
|||
sops.secrets.colin-passwd.neededForUsers = true;
|
||||
|
||||
sane.programs.sway.enableFor.user.colin = true;
|
||||
sane.programs.sway.config.mod = "Mod1"; #< alt key instead of Super
|
||||
sane.programs.blueberry.enableFor.user.colin = false; # bluetooth manager: doesn't cross compile!
|
||||
sane.programs.fcitx5.enableFor.user.colin = false; # does not cross compile
|
||||
sane.programs.mercurial.enableFor.user.colin = false; # does not cross compile
|
||||
|
@ -40,8 +40,8 @@
|
|||
|
||||
# sane.programs.ntfy-sh.config.autostart = true;
|
||||
sane.programs.dino.config.autostart = true;
|
||||
sane.programs.signal-desktop.config.autostart = true;
|
||||
# sane.programs.geary.config.autostart = true;
|
||||
# sane.programs.signal-desktop.config.autostart = true; # TODO: enable once electron stops derping.
|
||||
# sane.programs."gnome.geary".config.autostart = true;
|
||||
# sane.programs.calls.config.autostart = true;
|
||||
|
||||
sane.programs.pipewire.config = {
|
||||
|
|
68
hosts/by-name/moby/gps.nix
Normal file
68
hosts/by-name/moby/gps.nix
Normal file
|
@ -0,0 +1,68 @@
|
|||
# pinephone GPS happens in EG25 modem
|
||||
# serial control interface to modem is /dev/ttyUSB2
|
||||
# after enabling GPS, readout is /dev/ttyUSB1
|
||||
#
|
||||
# minimal process to enable modem and GPS:
|
||||
# - `echo 1 > /sys/class/modem-power/modem-power/device/powered`
|
||||
# - `screen /dev/ttyUSB2 115200`
|
||||
# - `AT+QGPSCFG="nmeasrc",1`
|
||||
# - `AT+QGPS=1`
|
||||
# this process is automated by my `eg25-control` program and services (`eg25-control-powered`, `eg25-control-gps`)
|
||||
# - see the `modules/` directory further up this repository.
|
||||
#
|
||||
# now, something like `gpsd` can directly read from /dev/ttyUSB1,
|
||||
# or geoclue can query the GPS directly through modem-manager
|
||||
#
|
||||
# initial GPS fix can take 15+ minutes.
|
||||
# meanwhile, services like eg25-manager or eg25-control-freshen-agps can speed this up by uploading assisted GPS data to the modem.
|
||||
#
|
||||
# support/help:
|
||||
# - geoclue, gnome-maps
|
||||
# - irc: #gnome-maps on irc.gimp.org
|
||||
# - Matrix: #gnome-maps:gnome.org (unclear if bridged to IRC)
|
||||
#
|
||||
# programs to pair this with:
|
||||
# - `satellite-gtk`: <https://codeberg.org/tpikonen/satellite>
|
||||
# - shows/tracks which satellites the GPS is connected to; useful to understand fix characteristics
|
||||
# - `gnome-maps`: uses geoclue, has route planning
|
||||
# - `mepo`: uses gpsd, minimalist, flaky, and buttons are kinda hard to activate on mobile
|
||||
# - puremaps?
|
||||
# - osmin?
|
||||
#
|
||||
# known/outstanding bugs:
|
||||
# - `systemctl start eg25-control-gps` can the hang the whole system (2023/10/06)
|
||||
# - i think it's actually `eg25-control-powered` which does this (started by the gps)
|
||||
# - best guess is modem draws so much power at launch that other parts of the system see undervoltage
|
||||
# - workaround is to hard power-cycle the system. the modem may not bring up after reboot: leave unpowered for 60s and boot again.
|
||||
#
|
||||
# future work:
|
||||
# - integrate with [wigle](https://www.wigle.net/) for offline equivalent to Mozilla Location Services
|
||||
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
# test gpsd with `gpspipe -w -n 10 2> /dev/null | grep -m 1 TPV | jq '.lat, .lon' | tr '\n' ' '`
|
||||
# ^ should return <lat> <long>
|
||||
services.gpsd.enable = true;
|
||||
services.gpsd.devices = [ "/dev/ttyUSB1" ];
|
||||
|
||||
# test geoclue2 by building `geoclue2-with-demo-agent`
|
||||
# and running "${geoclue2-with-demo-agent}/libexec/geoclue-2.0/demos/where-am-i"
|
||||
# note that geoclue is dbus-activated, and auto-stops after 60s with no caller
|
||||
services.geoclue2.enable = true;
|
||||
services.geoclue2.appConfig.where-am-i = {
|
||||
# this is the default "agent", shipped by geoclue package: allow it to use location
|
||||
isAllowed = true;
|
||||
isSystem = false;
|
||||
# XXX: setting users != [] might be causing `where-am-i` to time out
|
||||
users = [
|
||||
# restrict to only one set of users. empty array (default) means "allow any user to access geolocation".
|
||||
(builtins.toString config.users.users.colin.uid)
|
||||
];
|
||||
};
|
||||
systemd.services.geoclue.after = lib.mkForce []; #< defaults to network-online, but not all my sources require network
|
||||
users.users.geoclue.extraGroups = [
|
||||
"dialout" # TODO: figure out if dialout is required. that's for /dev/ttyUSB1, but geoclue probably doesn't read that?
|
||||
];
|
||||
|
||||
sane.programs.where-am-i.enableFor.user.colin = true;
|
||||
}
|
|
@ -3,19 +3,9 @@
|
|||
let
|
||||
portOpts = with lib; types.submodule {
|
||||
options = {
|
||||
visibleTo.ovpns = mkOption {
|
||||
visibleTo.ovpn = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
whether to forward inbound traffic on the OVPN vpn port to the corresponding localhost port.
|
||||
'';
|
||||
};
|
||||
visibleTo.doof = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
whether to forward inbound traffic on the doofnet vpn port to the corresponding localhost port.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -23,7 +13,7 @@ in
|
|||
{
|
||||
options = with lib; {
|
||||
sane.ports.ports = mkOption {
|
||||
# add the `visibleTo.{doof,ovpns}` options
|
||||
# add the `visibleTo.ovpn` option
|
||||
type = types.attrsOf portOpts;
|
||||
};
|
||||
};
|
||||
|
@ -50,16 +40,18 @@ in
|
|||
|
||||
# tun-sea config
|
||||
sane.dns.zones."uninsane.org".inet.A."doof.tunnel" = "205.201.63.12";
|
||||
# sane.dns.zones."uninsane.org".inet.AAAA."doof.tunnel" = "2602:fce8:106::51"; #< TODO: enable IPv6
|
||||
networking.wireguard.interfaces.wg-doof = {
|
||||
sane.dns.zones."uninsane.org".inet.AAAA."doof.tunnel" = "2602:fce8:106::51";
|
||||
networking.wireguard.interfaces.wg-doof = let
|
||||
ip = "${pkgs.iproute2}/bin/ip";
|
||||
in {
|
||||
privateKeyFile = config.sops.secrets.wg_doof_privkey.path;
|
||||
# wg is active only in this namespace.
|
||||
# run e.g. ip netns exec doof <some command like ping/curl/etc, it'll go through wg>
|
||||
# sudo ip netns exec doof ping www.google.com
|
||||
interfaceNamespace = "doof";
|
||||
ips = [
|
||||
"205.201.63.12"
|
||||
# "2602:fce8:106::51/128" #< TODO: enable IPv6
|
||||
"205.201.63.12/32"
|
||||
"2602:fce8:106::51/128"
|
||||
];
|
||||
peers = [
|
||||
{
|
||||
|
@ -71,24 +63,45 @@ in
|
|||
persistentKeepalive = 25; #< keep the NAT alive
|
||||
}
|
||||
];
|
||||
preSetup = ''
|
||||
${ip} netns add doof || (test -e /run/netns/doof && echo "doof already exists")
|
||||
'';
|
||||
postShutdown = ''
|
||||
${ip} netns delete doof || echo "couldn't delete doof"
|
||||
'';
|
||||
};
|
||||
sane.netns.doof.hostVethIpv4 = "10.0.2.5";
|
||||
sane.netns.doof.netnsVethIpv4 = "10.0.2.6";
|
||||
sane.netns.doof.netnsPubIpv4 = "205.201.63.12";
|
||||
sane.netns.doof.routeTable = 12;
|
||||
|
||||
# OVPN CONFIG (https://www.ovpn.com):
|
||||
# DOCS: https://nixos.wiki/wiki/WireGuard
|
||||
# if you `systemctl restart wireguard-wg-ovpns`, make sure to also restart any other services in `NetworkNamespacePath = .../ovpns`.
|
||||
# TODO: why not create the namespace as a seperate operation (nix config for that?)
|
||||
networking.wireguard.enable = true;
|
||||
networking.wireguard.interfaces.wg-ovpns = {
|
||||
networking.wireguard.interfaces.wg-ovpns = let
|
||||
ip = "${pkgs.iproute2}/bin/ip";
|
||||
in-ns = "${ip} netns exec ovpns";
|
||||
iptables = "${pkgs.iptables}/bin/iptables";
|
||||
veth-host-ip = "10.0.1.5";
|
||||
veth-local-ip = "10.0.1.6";
|
||||
vpn-ip = "185.157.162.178";
|
||||
# DNS = 46.227.67.134, 192.165.9.158, 2a07:a880:4601:10f0:cd45::1, 2001:67c:750:1:cafe:cd45::1
|
||||
vpn-dns = "46.227.67.134";
|
||||
bridgePort = port: proto: ''
|
||||
${in-ns} ${iptables} -A PREROUTING -t nat -p ${proto} --dport ${port} -m iprange --dst-range ${vpn-ip} \
|
||||
-j DNAT --to-destination ${veth-host-ip}
|
||||
'';
|
||||
bridgeStatements = lib.foldlAttrs
|
||||
(acc: port: portCfg: acc ++ (builtins.map (bridgePort port) portCfg.protocol))
|
||||
[]
|
||||
config.sane.ports.ports;
|
||||
in {
|
||||
privateKeyFile = config.sops.secrets.wg_ovpns_privkey.path;
|
||||
# wg is active only in this namespace.
|
||||
# run e.g. ip netns exec ovpns <some command like ping/curl/etc, it'll go through wg>
|
||||
# sudo ip netns exec ovpns ping www.google.com
|
||||
interfaceNamespace = "ovpns";
|
||||
ips = [ "185.157.162.178" ];
|
||||
ips = [
|
||||
"185.157.162.178/32"
|
||||
];
|
||||
peers = [
|
||||
{
|
||||
publicKey = "SkkEZDCBde22KTs/Hc7FWvDBfdOCQA4YtBEuC3n5KGs=";
|
||||
|
@ -106,11 +119,99 @@ in
|
|||
# dynamicEndpointRefreshRestartSeconds = 5;
|
||||
}
|
||||
];
|
||||
preSetup = ''
|
||||
${ip} netns add ovpns || (test -e /run/netns/ovpns && echo "ovpns already exists")
|
||||
'';
|
||||
postShutdown = ''
|
||||
${in-ns} ip link del ovpns-veth-b || echo "couldn't delete ovpns-veth-b"
|
||||
${ip} link del ovpns-veth-a || echo "couldn't delete ovpns-veth-a"
|
||||
${ip} netns delete ovpns || echo "couldn't delete ovpns"
|
||||
# restore rules/routes
|
||||
${ip} rule del from ${veth-host-ip} lookup ovpns pref 50 || echo "couldn't delete init -> ovpns rule"
|
||||
${ip} route del default via ${veth-local-ip} dev ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns || echo "couldn't delete init -> ovpns route"
|
||||
${ip} rule add from all lookup local pref 0
|
||||
${ip} rule del from all lookup local pref 100
|
||||
'';
|
||||
postSetup = ''
|
||||
# DOCS:
|
||||
# - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/>
|
||||
# - iptables primer: <https://danielmiessler.com/study/iptables/>
|
||||
# create veth pair
|
||||
${ip} link add ovpns-veth-a type veth peer name ovpns-veth-b
|
||||
${ip} addr add ${veth-host-ip}/24 dev ovpns-veth-a
|
||||
${ip} link set ovpns-veth-a up
|
||||
|
||||
# mv veth-b into the ovpns namespace
|
||||
${ip} link set ovpns-veth-b netns ovpns
|
||||
${in-ns} ip addr add ${veth-local-ip}/24 dev ovpns-veth-b
|
||||
${in-ns} ip link set ovpns-veth-b up
|
||||
|
||||
# make it so traffic originating from the host side of the veth
|
||||
# is sent over the veth no matter its destination.
|
||||
${ip} rule add from ${veth-host-ip} lookup ovpns pref 50
|
||||
# for traffic originating at the host veth to the WAN, use the veth as our gateway
|
||||
# not sure if the metric 1002 matters.
|
||||
${ip} route add default via ${veth-local-ip} dev ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns
|
||||
# give the default route lower priority
|
||||
${ip} rule add from all lookup local pref 100
|
||||
${ip} rule del from all lookup local pref 0
|
||||
|
||||
# in order to access DNS in this netns, we need to route it to the VPN's nameservers
|
||||
# - alternatively, we could fix DNS servers like 1.1.1.1.
|
||||
${in-ns} ${iptables} -A OUTPUT -t nat -p udp --dport 53 -m iprange --dst-range 127.0.0.53 \
|
||||
-j DNAT --to-destination ${vpn-dns}:53
|
||||
'' + (lib.concatStringsSep "\n" bridgeStatements);
|
||||
};
|
||||
sane.netns.ovpns.hostVethIpv4 = "10.0.1.5";
|
||||
sane.netns.ovpns.netnsVethIpv4 = "10.0.1.6";
|
||||
sane.netns.ovpns.netnsPubIpv4 = "185.157.162.178";
|
||||
sane.netns.ovpns.routeTable = 11;
|
||||
sane.netns.ovpns.dns = "46.227.67.134"; #< DNS requests inside the namespace are forwarded here
|
||||
|
||||
# create a new routing table that we can use to proxy traffic out of the root namespace
|
||||
# through the ovpns namespace, and to the WAN via VPN.
|
||||
networking.iproute2.rttablesExtraConfig = ''
|
||||
5 ovpns
|
||||
'';
|
||||
networking.iproute2.enable = true;
|
||||
|
||||
|
||||
# HURRICANE ELECTRIC CONFIG:
|
||||
# networking.sits = {
|
||||
# hurricane = {
|
||||
# remote = "216.218.226.238";
|
||||
# local = "192.168.0.5";
|
||||
# # local = "10.0.0.5";
|
||||
# # remote = "10.0.0.1";
|
||||
# # local = "10.0.0.22";
|
||||
# dev = "eth0";
|
||||
# ttl = 255;
|
||||
# };
|
||||
# };
|
||||
# networking.interfaces."hurricane".ipv6 = {
|
||||
# addresses = [
|
||||
# # mx.uninsane.org (publically routed /64)
|
||||
# {
|
||||
# address = "2001:470:b:465::1";
|
||||
# prefixLength = 128;
|
||||
# }
|
||||
# # client addr
|
||||
# # {
|
||||
# # address = "2001:470:a:466::2";
|
||||
# # prefixLength = 64;
|
||||
# # }
|
||||
# ];
|
||||
# routes = [
|
||||
# {
|
||||
# address = "::";
|
||||
# prefixLength = 0;
|
||||
# # via = "2001:470:a:466::1";
|
||||
# }
|
||||
# ];
|
||||
# };
|
||||
|
||||
# # after configuration, we want the hurricane device to look like this:
|
||||
# # hurricane: flags=209<UP,POINTOPOINT,RUNNING,NOARP> mtu 1480
|
||||
# # inet6 2001:470:a:450::2 prefixlen 64 scopeid 0x0<global>
|
||||
# # inet6 fe80::c0a8:16 prefixlen 64 scopeid 0x20<link>
|
||||
# # sit txqueuelen 1000 (IPv6-in-IPv4)
|
||||
# # test with:
|
||||
# # curl --interface hurricane http://[2607:f8b0:400a:80b::2004]
|
||||
# # ping 2607:f8b0:400a:80b::2004
|
||||
};
|
||||
}
|
||||
|
|
34
hosts/by-name/servo/services/calibre.nix
Normal file
34
hosts/by-name/servo/services/calibre.nix
Normal file
|
@ -0,0 +1,34 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
cweb-cfg = config.services.calibre-web;
|
||||
inherit (cweb-cfg) user group;
|
||||
inherit (cweb-cfg.listen) ip port;
|
||||
svc-dir = "/var/lib/${cweb-cfg.dataDir}";
|
||||
in
|
||||
# XXX: disabled because of runtime errors like:
|
||||
# > File "/nix/store/c7jqvx980nlg9xhxi065cba61r2ain9y-calibre-web-0.6.19/lib/python3.10/site-packages/calibreweb/cps/db.py", line 926, in speaking_language
|
||||
# > languages = self.session.query(Languages) \
|
||||
# > AttributeError: 'NoneType' object has no attribute 'query'
|
||||
lib.mkIf false
|
||||
{
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
{ inherit user group; mode = "0700"; path = svc-dir; method = "bind"; }
|
||||
];
|
||||
|
||||
services.calibre-web.enable = true;
|
||||
services.calibre-web.listen.ip = "127.0.0.1";
|
||||
# XXX: externally populate `${svc-dir}/metadata.db` (once) from
|
||||
# <https://github.com/janeczku/calibre-web/blob/master/library/metadata.db>
|
||||
# i don't know why you have to do this??
|
||||
# services.calibre-web.options.calibreLibrary = svc-dir;
|
||||
|
||||
services.nginx.virtualHosts."calibre.uninsane.org" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
locations."/" = {
|
||||
proxyPass = "http://${ip}:${builtins.toString port}";
|
||||
};
|
||||
};
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."calibre" = "native";
|
||||
}
|
|
@ -36,8 +36,7 @@
|
|||
# - rb = received bytes
|
||||
# - sp = sent packets
|
||||
# - sb = sent bytes
|
||||
|
||||
{ config, lib, ... }:
|
||||
{ lib, ... }:
|
||||
let
|
||||
# TURN port range (inclusive).
|
||||
# default coturn behavior is to use the upper quarter of all ports. i.e. 49152 - 65535.
|
||||
|
@ -56,7 +55,7 @@ in
|
|||
# protocol = [ "tcp" "udp" ];
|
||||
# # visibleTo.lan = true;
|
||||
# # visibleTo.wan = true;
|
||||
# visibleTo.ovpns = true; # forward traffic from the VPN to the root NS
|
||||
# visibleTo.ovpn = true; # forward traffic from the VPN to the root NS
|
||||
# description = "colin-stun-turn";
|
||||
# };
|
||||
# "5349" = {
|
||||
|
@ -64,7 +63,7 @@ in
|
|||
# protocol = [ "tcp" ];
|
||||
# # visibleTo.lan = true;
|
||||
# # visibleTo.wan = true;
|
||||
# visibleTo.ovpns = true;
|
||||
# visibleTo.ovpn = true;
|
||||
# description = "colin-stun-turn-over-tls";
|
||||
# };
|
||||
# }
|
||||
|
@ -77,7 +76,7 @@ in
|
|||
# protocol = [ "tcp" "udp" ];
|
||||
# # visibleTo.lan = true;
|
||||
# # visibleTo.wan = true;
|
||||
# visibleTo.ovpns = true;
|
||||
# visibleTo.ovpn = true;
|
||||
# description = "colin-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
|
||||
# };
|
||||
# })
|
||||
|
@ -131,11 +130,11 @@ in
|
|||
"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=${config.sane.netns.ovpns.hostVethIpv4}" "external-ip=${config.sane.netns.ovpns.netnsPubIpv4}" #< 2024/04/25: works, if running in root namespace
|
||||
"listening-ip=${config.sane.netns.ovpns.netnsPubIpv4}" "external-ip=${config.sane.netns.ovpns.netnsPubIpv4}"
|
||||
# "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:
|
||||
# "external-ip=${config.sane.netns.ovpns.netnsPubIpv4}/${config.sane.netns.ovpns.hostVethIpv4}"
|
||||
# "external-ip=185.157.162.178/10.0.1.5"
|
||||
# "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"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p pyln-client -p python3
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ps.pyln-client ])"
|
||||
|
||||
"""
|
||||
clightning-sane: helper to perform common Lightning node admin operations:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./calibre.nix
|
||||
./coturn.nix
|
||||
./cryptocurrencies
|
||||
./email
|
||||
|
@ -25,7 +26,7 @@
|
|||
./postgres.nix
|
||||
./prosody
|
||||
./slskd.nix
|
||||
./transmission
|
||||
./transmission.nix
|
||||
./trust-dns.nix
|
||||
./wikipedia.nix
|
||||
];
|
||||
|
|
|
@ -51,54 +51,54 @@ lib.mkIf false
|
|||
{
|
||||
"3478" = {
|
||||
protocol = [ "tcp" "udp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-stun-turn";
|
||||
};
|
||||
"5222" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-client-to-server";
|
||||
};
|
||||
"5223" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpps-client-to-server"; # XMPP over TLS
|
||||
};
|
||||
"5269" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-server-to-server";
|
||||
};
|
||||
"5270" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpps-server-to-server"; # XMPP over TLS
|
||||
};
|
||||
"5280" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-bosh";
|
||||
};
|
||||
"5281" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-bosh-https";
|
||||
};
|
||||
"5349" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-stun-turn-over-tls";
|
||||
};
|
||||
"5443" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-web-services"; # file uploads, websockets, admin
|
||||
};
|
||||
}
|
||||
|
@ -109,8 +109,8 @@ lib.mkIf false
|
|||
numPorts = turnPortHigh - turnPortLow + 1;
|
||||
in {
|
||||
protocol = [ "tcp" "udp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
|
||||
};
|
||||
})
|
||||
|
|
|
@ -8,14 +8,14 @@
|
|||
{
|
||||
sane.ports.ports."143" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-imap-imap.uninsane.org";
|
||||
};
|
||||
sane.ports.ports."993" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-imaps-imap.uninsane.org";
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# postfix config options: <https://www.postfix.org/postconf.5.html>
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
submissionOptions = {
|
||||
|
@ -56,7 +56,8 @@ in
|
|||
|
||||
sane.dns.zones."uninsane.org".inet = {
|
||||
MX."@" = "10 mx.uninsane.org.";
|
||||
A."mx" = "%AOVPNS%"; #< XXX: RFC's specify that the MX record CANNOT BE A CNAME. TODO: use "%AOVPNS%?
|
||||
# XXX: RFC's specify that the MX record CANNOT BE A CNAME
|
||||
A."mx" = "185.157.162.178";
|
||||
|
||||
# Sender Policy Framework:
|
||||
# +mx => mail passes if it originated from the MX
|
||||
|
|
|
@ -12,10 +12,6 @@
|
|||
device = "/var/media";
|
||||
options = [ "rbind" ];
|
||||
};
|
||||
fileSystems."/var/export/pub" = {
|
||||
device = "/var/www/sites/uninsane.org/share";
|
||||
options = [ "rbind" ];
|
||||
};
|
||||
# fileSystems."/var/export/playground" = {
|
||||
# device = config.fileSystems."/mnt/persist/ext".device;
|
||||
# fsType = "btrfs";
|
||||
|
@ -41,8 +37,7 @@
|
|||
wantedBy = [ "nfs.service" "sftpgo.service" ];
|
||||
file.text = ''
|
||||
- media/ read-only: Videos, Music, Books, etc
|
||||
- playground/ read-write: use it to share files with other users of this server, inaccessible from the www
|
||||
- pub/ read-only: content made to be shared with the www
|
||||
- playground/ read-write: use it to share files with other users of this server
|
||||
'';
|
||||
};
|
||||
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
let
|
||||
external_auth_hook = pkgs.static-nix-shell.mkPython3 {
|
||||
external_auth_hook = pkgs.static-nix-shell.mkPython3Bin {
|
||||
pname = "external_auth_hook";
|
||||
srcRoot = ./.;
|
||||
pkgs = [ "python3.pkgs.passlib" ];
|
||||
pyPkgs = [ "passlib" ];
|
||||
};
|
||||
# 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
|
||||
|
@ -27,12 +27,13 @@ in
|
|||
"21" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
# visibleTo.wan = true;
|
||||
description = "colin-FTP server";
|
||||
};
|
||||
"990" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-FTPS server";
|
||||
};
|
||||
} // (sane-lib.mapToAttrs
|
||||
|
@ -40,8 +41,8 @@ in
|
|||
name = builtins.toString port;
|
||||
value = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-FTP server data port range";
|
||||
};
|
||||
})
|
||||
|
@ -100,13 +101,6 @@ in
|
|||
debug = true;
|
||||
tls_mode = 2; # 2 = "implicit FTPS": client negotiates TLS before any FTP command.
|
||||
}
|
||||
{
|
||||
# binding this means any doof client can connect (TLS only)
|
||||
address = config.sane.netns.doof.hostVethIpv4;
|
||||
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
|
||||
|
@ -123,7 +117,7 @@ in
|
|||
banner = ''
|
||||
Welcome, friends, to Colin's FTP server! Also available via NFS on the same host, but LAN-only.
|
||||
|
||||
Read-only access (LAN clients see everything; WAN clients can only see /pub):
|
||||
Read-only access (LAN-restricted):
|
||||
Username: "anonymous"
|
||||
Password: "anonymous"
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p python3 -p python3.pkgs.passlib
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ps.passlib ])"
|
||||
# vim: set filetype=python :
|
||||
#
|
||||
# available environment variables:
|
||||
|
@ -45,8 +45,6 @@ from hmac import compare_digest
|
|||
|
||||
authFail = dict(username="")
|
||||
|
||||
PERM_DENY = []
|
||||
PERM_LIST = [ "list" ]
|
||||
PERM_RO = [ "list", "download" ]
|
||||
PERM_RW = [
|
||||
# read-only:
|
||||
|
@ -129,14 +127,12 @@ def getAuthResponse(ip: str, username: str, password: str) -> dict:
|
|||
return mkAuthOk(username, permissions = {
|
||||
"/": PERM_RW,
|
||||
"/playground": PERM_RW,
|
||||
"/pub": PERM_RO,
|
||||
})
|
||||
if isWireguard(ip):
|
||||
# allow any user from wireguard
|
||||
return mkAuthOk(username, permissions = {
|
||||
"/": PERM_RW,
|
||||
"/playground": PERM_RW,
|
||||
"/pub": PERM_RO,
|
||||
})
|
||||
if isLan(ip):
|
||||
if username == "anonymous":
|
||||
|
@ -144,19 +140,7 @@ def getAuthResponse(ip: str, username: str, password: str) -> dict:
|
|||
return mkAuthOk("anonymous", permissions = {
|
||||
"/": PERM_RO,
|
||||
"/playground": PERM_RW,
|
||||
"/pub": PERM_RO,
|
||||
})
|
||||
if username == "anonymous":
|
||||
# anonymous users from the www can have even more limited access.
|
||||
# mostly because i need an easy way to test WAN connectivity :-)
|
||||
return mkAuthOk("anonymous", permissions = {
|
||||
# "/": PERM_DENY,
|
||||
"/": PERM_LIST, #< REQUIRED, even for lftp to list a subdir
|
||||
"/media": PERM_DENY,
|
||||
"/playground": PERM_DENY,
|
||||
"/pub": PERM_RO,
|
||||
# "/README.md": PERM_RO, #< does not work
|
||||
})
|
||||
|
||||
return authFail
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
# ```
|
||||
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
lib.mkIf false #< 2024/07/04: i haven't actively used this for months
|
||||
{
|
||||
sops.secrets."freshrss_passwd" = {
|
||||
owner = config.users.users.freshrss.name;
|
||||
|
|
|
@ -133,7 +133,7 @@
|
|||
sane.ports.ports."22" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-git@git.uninsane.org";
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{ lib, pkgs, ... }:
|
||||
|
||||
lib.mkIf false #< TODO: re-enable once confident of sandboxing
|
||||
{
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
# TODO: mode? we only need this to save Indexer creds ==> migrate to config?
|
||||
|
@ -12,7 +13,7 @@
|
|||
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 ${config.sane.netns.ovpns.netnsPubIpv4}" ]; # abort if public IP is not as expected
|
||||
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";
|
||||
|
@ -24,7 +25,8 @@
|
|||
enableACME = true;
|
||||
# inherit kTLS;
|
||||
locations."/" = {
|
||||
proxyPass = "http://${config.sane.netns.ovpns.netnsVethIpv4}:9117";
|
||||
# proxyPass = "http://ovpns.uninsane.org:9117";
|
||||
proxyPass = "http://10.0.1.6:9117";
|
||||
recommendedProxySettings = true;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
ircServer = { name, additionalAddresses ? [], ssl ? true, sasl ? true, port ? if ssl then 6697 else 6667 }: let
|
||||
ircServer = { name, additionalAddresses ? [], sasl ? true, port ? 6697 }: let
|
||||
lowerName = lib.toLower name;
|
||||
in {
|
||||
# XXX sasl: appservice doesn't support NickServ identification (only SASL, or PASS if sasl = false)
|
||||
inherit additionalAddresses name port sasl ssl;
|
||||
inherit name additionalAddresses sasl port;
|
||||
ssl = true;
|
||||
botConfig = {
|
||||
# bot has no presence in IRC channel; only real Matrix users
|
||||
enabled = false;
|
||||
|
@ -155,10 +156,6 @@ in
|
|||
# - #sxmo-offtopic
|
||||
};
|
||||
"irc.rizon.net" = ircServer { name = "Rizon"; };
|
||||
"wigle.net" = ircServer {
|
||||
name = "WiGLE";
|
||||
ssl = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -17,14 +17,14 @@ in
|
|||
sane.ports.ports."80" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.ovpns = true; # so that letsencrypt can procure a cert for the mx record
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
visibleTo.ovpn = true; # so that letsencrypt can procure a cert for the mx record
|
||||
description = "colin-http-uninsane.org";
|
||||
};
|
||||
sane.ports.ports."443" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-https-uninsane.org";
|
||||
};
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ in
|
|||
sane.ports.ports."${builtins.toString altPort}" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-ntfy.uninsane.org";
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p ntfy-sh -p python3
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p ntfy-sh
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
|
|
|
@ -47,7 +47,7 @@ in
|
|||
};
|
||||
sane.ntfy-waiter.package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.static-nix-shell.mkPython3 {
|
||||
default = pkgs.static-nix-shell.mkPython3Bin {
|
||||
pname = "ntfy-waiter";
|
||||
srcRoot = ./.;
|
||||
pkgs = [ "ntfy-sh" ];
|
||||
|
@ -62,8 +62,8 @@ in
|
|||
sane.ports.ports = lib.mkMerge (lib.forEach portRange (port: {
|
||||
"${builtins.toString port}" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-notification-waiter-${builtins.toString (port - portLow + 1)}-of-${builtins.toString numPorts}";
|
||||
};
|
||||
}));
|
||||
|
|
|
@ -61,42 +61,42 @@ in
|
|||
];
|
||||
sane.ports.ports."5000" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-prosody-fileshare-proxy65";
|
||||
};
|
||||
sane.ports.ports."5222" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-client-to-server";
|
||||
};
|
||||
sane.ports.ports."5223" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpps-client-to-server"; # XMPP over TLS
|
||||
};
|
||||
sane.ports.ports."5269" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-server-to-server";
|
||||
};
|
||||
sane.ports.ports."5270" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpps-server-to-server"; # XMPP over TLS
|
||||
};
|
||||
sane.ports.ports."5280" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-bosh";
|
||||
};
|
||||
sane.ports.ports."5281" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.doof = true;
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-prosody-https"; # necessary?
|
||||
};
|
||||
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
|
||||
sane.ports.ports."50300" = {
|
||||
protocol = [ "tcp" ];
|
||||
# visibleTo.ovpns = true; #< not needed: it runs in the ovpns namespace
|
||||
# not visible to WAN: i run this in a separate netns
|
||||
visibleTo.ovpn = true;
|
||||
description = "colin-soulseek";
|
||||
};
|
||||
|
||||
|
@ -32,7 +33,7 @@
|
|||
forceSSL = true;
|
||||
enableACME = true;
|
||||
locations."/" = {
|
||||
proxyPass = "http://${config.sane.netns.ovpns.netnsVethIpv4}:5030";
|
||||
proxyPass = "http://10.0.1.6:5030";
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
};
|
||||
|
@ -71,7 +72,7 @@
|
|||
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 ${config.sane.netns.ovpns.netnsPubIpv4}" ]; # abort if public IP is not as expected
|
||||
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";
|
||||
|
|
|
@ -22,19 +22,67 @@ let
|
|||
--replace-fail 'set(TR_USER_AGENT_PREFIX "''${TR_SEMVER}")' 'set(TR_USER_AGENT_PREFIX "3.00")'
|
||||
'';
|
||||
});
|
||||
download-dir = "/var/media/torrents"; #< keep in sync with consts embedded in `torrent-done`
|
||||
torrent-done = pkgs.static-nix-shell.mkBash {
|
||||
pname = "torrent-done";
|
||||
srcRoot = ./.;
|
||||
pkgs = [
|
||||
"acl"
|
||||
"coreutils"
|
||||
"findutils"
|
||||
"rsync"
|
||||
"util-linux"
|
||||
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
|
||||
lib.mkIf false #< TODO: re-enable once confident of sandboxing
|
||||
{
|
||||
sane.persist.sys.byStore.plaintext = [
|
||||
# TODO: mode? we need this specifically for the stats tracking in .config/
|
||||
|
@ -58,8 +106,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.
|
||||
# ovpns.netnsVethIpv4 => allow rpc only from the root servo ns. it'll tunnel things to the net, if need be.
|
||||
rpc-bind-address = config.sane.netns.ovpns.netnsVethIpv4;
|
||||
# 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";
|
||||
#rpc-host-whitelist = "bt.uninsane.org";
|
||||
#rpc-whitelist = "*.*.*.*";
|
||||
rpc-authentication-required = true;
|
||||
|
@ -70,7 +118,7 @@ in
|
|||
rpc-whitelist-enabled = false;
|
||||
|
||||
# force behind ovpns in case the NetworkNamespace fails somehow
|
||||
bind-address-ipv4 = config.sane.netns.ovpns.netnsPubIpv4;
|
||||
bind-address-ipv4 = "185.157.162.178";
|
||||
port-forwarding-enabled = false;
|
||||
|
||||
# hopefully, make the downloads world-readable
|
||||
|
@ -112,7 +160,7 @@ 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 ${config.sane.netns.ovpns.netnsPubIpv4}" ]; # abort if public IP is not as expected
|
||||
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";
|
||||
|
@ -142,14 +190,14 @@ in
|
|||
# inherit kTLS;
|
||||
locations."/" = {
|
||||
# proxyPass = "http://ovpns.uninsane.org:9091";
|
||||
proxyPass = "http://${config.sane.netns.ovpns.netnsVethIpv4}:9091";
|
||||
proxyPass = "http://10.0.1.6:9091";
|
||||
};
|
||||
};
|
||||
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."bt" = "native";
|
||||
sane.ports.ports."51413" = {
|
||||
protocol = [ "tcp" "udp" ];
|
||||
# visibleTo.ovpns = true; #< not needed: it runs in the ovpns namespace
|
||||
visibleTo.ovpn = true;
|
||||
description = "colin-bittorrent";
|
||||
};
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p acl -p bash -p coreutils -p findutils -p rsync -p util-linux
|
||||
|
||||
# transmission invokes this with no args, and the following env vars:
|
||||
# - TR_TORRENT_DIR: full path to the folder i told transmission to download it to.
|
||||
# e.g. /var/media/torrents/Videos/Film/Jason.Bourne-2016
|
||||
# optionally:
|
||||
# - TR_DRY_RUN=1
|
||||
# - TR_DEBUG=1
|
||||
# - TR_NO_HARDLINK=1
|
||||
|
||||
DOWNLOAD_DIR=/var/media/torrents
|
||||
|
||||
destructive() {
|
||||
if [ -n "${TR_DRY_RUN-}" ]; then
|
||||
echo "[dry-run] $*"
|
||||
else
|
||||
debug "$@"
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
debug() {
|
||||
if [ -n "${TR_DEBUG-}" ]; then
|
||||
echo "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "TR_TORRENT_DIR=$TR_TORRENT_DIR torrent-done $*"
|
||||
|
||||
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"/*)
|
||||
debug "top-level items in torrent dir:" "${subdirs[@]}"
|
||||
if [ ${#subdirs[@]} -eq 1 ]; then
|
||||
dirname="${subdirs[0]}"
|
||||
debug "exactly one top-level item, checking if directory: $dirname"
|
||||
if [ -d "$dirname" ]; then
|
||||
destructive mv "$dirname"/* "$MEDIA_DIR/" && destructive rmdir "$dirname"
|
||||
fi
|
||||
fi
|
||||
|
||||
# remove noisy files:
|
||||
destructive find "$MEDIA_DIR/" -type f \(\
|
||||
-iname '.*downloaded.?from.*' \
|
||||
-o -iname 'source.txt' \
|
||||
-o -iname 'upcoming.?releases.*' \
|
||||
-o -iname 'www.YTS.*.jpg' \
|
||||
-o -iname 'WWW.YIFY*.COM.jpg' \
|
||||
-o -iname 'YIFY*.com.txt' \
|
||||
-o -iname 'YTS*.com.txt' \
|
||||
\) -exec rm {} \;
|
||||
|
||||
if ! [ -n "${TR_NO_HARDLINK}" ]; then
|
||||
# dedupe the whole media library.
|
||||
# yeah, a bit excessive: move this to a cron job if that's problematic
|
||||
# or make it run with only 1/N probability, etc.
|
||||
destructive hardlink /var/media --reflink=always --ignore-time --verbose
|
||||
fi
|
|
@ -4,14 +4,14 @@
|
|||
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
|
||||
{
|
||||
sane.ports.ports."53" = {
|
||||
protocol = [ "udp" "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
# visibleTo.wan = true;
|
||||
visibleTo.ovpns = true;
|
||||
visibleTo.doof = true;
|
||||
visibleTo.wan = true;
|
||||
visibleTo.ovpn = true;
|
||||
description = "colin-dns-hosting";
|
||||
};
|
||||
|
||||
|
@ -39,7 +39,6 @@ in
|
|||
CNAME."native" = "%CNAMENATIVE%";
|
||||
A."@" = "%ANATIVE%";
|
||||
A."servo.wan" = "%AWAN%";
|
||||
A."servo.doof" = "%ADOOF%";
|
||||
A."servo.lan" = config.sane.hosts.by-name."servo".lan-ip;
|
||||
A."servo.hn" = config.sane.hosts.by-name."servo".wg-home.ip;
|
||||
|
||||
|
@ -47,9 +46,9 @@ in
|
|||
# it's best that we keep this identical, or a superset of, what org. lists as our NS.
|
||||
# so, org. can specify ns2/ns3 as being to the VPN, with no mention of ns1. we provide ns1 here.
|
||||
A."ns1" = "%ANATIVE%";
|
||||
A."ns2" = "%ADOOF%";
|
||||
A."ns3" = "%AOVPNS%";
|
||||
A."ovpns" = "%AOVPNS%";
|
||||
A."ns2" = "185.157.162.178";
|
||||
A."ns3" = "185.157.162.178";
|
||||
A."ovpns" = "185.157.162.178";
|
||||
NS."@" = [
|
||||
"ns1.uninsane.org."
|
||||
"ns2.uninsane.org."
|
||||
|
@ -60,64 +59,58 @@ in
|
|||
services.trust-dns.settings.zones = [ "uninsane.org" ];
|
||||
|
||||
|
||||
networking.nat.enable = true; #< TODO: try removing this?
|
||||
# networking.nat.extraCommands = ''
|
||||
# # redirect incoming DNS requests from LAN addresses
|
||||
# # to the LAN-specialized DNS service
|
||||
# # N.B.: use the `nixos-*` chains instead of e.g. PREROUTING
|
||||
# # because they get cleanly reset across activations or `systemctl restart firewall`
|
||||
# # instead of accumulating cruft
|
||||
# iptables -t nat -A nixos-nat-pre -p udp --dport 53 \
|
||||
# -m iprange --src-range 10.78.76.0-10.78.79.255 \
|
||||
# -j DNAT --to-destination :1053
|
||||
# iptables -t nat -A nixos-nat-pre -p tcp --dport 53 \
|
||||
# -m iprange --src-range 10.78.76.0-10.78.79.255 \
|
||||
# -j DNAT --to-destination :1053
|
||||
# '';
|
||||
# sane.ports.ports."1053" = {
|
||||
# # because the NAT above redirects in nixos-nat-pre, LAN requests behave as though they arrived on the external interface at the redirected port.
|
||||
# # TODO: try nixos-nat-post instead?
|
||||
# # TODO: or, don't NAT from port 53 -> port 1053, but rather nat from LAN addr to a loopback addr.
|
||||
# # - this is complicated in that loopback is a different interface than eth0, so rewriting the destination address would cause the packets to just be dropped by the interface
|
||||
# protocol = [ "udp" "tcp" ];
|
||||
# visibleTo.lan = true;
|
||||
# description = "colin-redirected-dns-for-lan-namespace";
|
||||
# };
|
||||
networking.nat.enable = true;
|
||||
networking.nat.extraCommands = ''
|
||||
# redirect incoming DNS requests from LAN addresses
|
||||
# to the LAN-specialized DNS service
|
||||
# N.B.: use the `nixos-*` chains instead of e.g. PREROUTING
|
||||
# because they get cleanly reset across activations or `systemctl restart firewall`
|
||||
# instead of accumulating cruft
|
||||
iptables -t nat -A nixos-nat-pre -p udp --dport 53 \
|
||||
-m iprange --src-range 10.78.76.0-10.78.79.255 \
|
||||
-j DNAT --to-destination :1053
|
||||
iptables -t nat -A nixos-nat-pre -p tcp --dport 53 \
|
||||
-m iprange --src-range 10.78.76.0-10.78.79.255 \
|
||||
-j DNAT --to-destination :1053
|
||||
'';
|
||||
sane.ports.ports."1053" = {
|
||||
# because the NAT above redirects in nixos-nat-pre, LAN requests behave as though they arrived on the external interface at the redirected port.
|
||||
# TODO: try nixos-nat-post instead?
|
||||
# TODO: or, don't NAT from port 53 -> port 1053, but rather nat from LAN addr to a loopback addr.
|
||||
# - this is complicated in that loopback is a different interface than eth0, so rewriting the destination address would cause the packets to just be dropped by the interface
|
||||
protocol = [ "udp" "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
description = "colin-redirected-dns-for-lan-namespace";
|
||||
};
|
||||
|
||||
|
||||
sane.services.trust-dns.enable = true;
|
||||
sane.services.trust-dns.instances = let
|
||||
mkSubstitutions = flavor: {
|
||||
"%ADOOF%" = config.sane.netns.doof.netnsPubIpv4;
|
||||
"%ANATIVE%" = nativeAddrs."servo.${flavor}";
|
||||
"%AOVPNS%" = config.sane.netns.ovpns.netnsPubIpv4;
|
||||
"%AWAN%" = "$(cat '${dyn-dns.ipPath}')";
|
||||
"%CNAMENATIVE%" = "servo.${flavor}";
|
||||
"%ANATIVE%" = nativeAddrs."servo.${flavor}";
|
||||
"%AOVPNS%" = "185.157.162.178";
|
||||
};
|
||||
in
|
||||
{
|
||||
doof = {
|
||||
substitutions = mkSubstitutions "doof";
|
||||
wan = {
|
||||
substitutions = mkSubstitutions "wan";
|
||||
listenAddrsIpv4 = [
|
||||
config.sane.netns.doof.hostVethIpv4
|
||||
config.sane.netns.ovpns.hostVethIpv4
|
||||
nativeAddrs."servo.lan"
|
||||
bindOvpn
|
||||
];
|
||||
};
|
||||
hn = {
|
||||
substitutions = mkSubstitutions "hn";
|
||||
listenAddrsIpv4 = [ nativeAddrs."servo.hn" ];
|
||||
};
|
||||
lan = {
|
||||
substitutions = mkSubstitutions "lan";
|
||||
listenAddrsIpv4 = [ nativeAddrs."servo.lan" ];
|
||||
# port = 1053;
|
||||
port = 1053;
|
||||
};
|
||||
hn = {
|
||||
substitutions = mkSubstitutions "hn";
|
||||
listenAddrsIpv4 = [ nativeAddrs."servo.hn" ];
|
||||
port = 1053;
|
||||
};
|
||||
# wan = {
|
||||
# substitutions = mkSubstitutions "wan";
|
||||
# listenAddrsIpv4 = [
|
||||
# nativeAddrs."servo.lan"
|
||||
# ];
|
||||
# };
|
||||
# hn-resolver = {
|
||||
# # don't need %AWAN% here because we forward to the hn instance.
|
||||
# listenAddrsIpv4 = [ nativeAddrs."servo.hn" ];
|
||||
|
@ -158,10 +151,9 @@ in
|
|||
};
|
||||
|
||||
sane.services.dyn-dns.restartOnChange = [
|
||||
"trust-dns-doof.service"
|
||||
"trust-dns-hn.service"
|
||||
"trust-dns-wan.service"
|
||||
"trust-dns-lan.service"
|
||||
# "trust-dns-wan.service"
|
||||
"trust-dns-hn.service"
|
||||
# "trust-dns-hn-resolver.service" # doesn't need restart because it doesn't know about WAN IP
|
||||
];
|
||||
}
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
# where to find good stuff?
|
||||
# - universal search/directory: <https://podcastindex.org>
|
||||
# - list of lists: <https://en.wikipedia.org/wiki/Category:Lists_of_podcasts>
|
||||
# - podcasts w/ a community: <https://lemmyverse.net/communities?query=podcast>
|
||||
# - podcast rec thread: <https://lemmy.ml/post/1565858>
|
||||
#
|
||||
# candidates:
|
||||
# - The Nonlinear Library (podcast): <https://forum.effectivealtruism.org/posts/JTZTBienqWEAjGDRv/listen-to-more-ea-content-with-the-nonlinear-library>
|
||||
# - has ~10 posts per day, text-to-speech; i would need better tagging before adding this
|
||||
# - <https://www.metaculus.com/questions/11102/introducing-the-metaculus-journal-podcast/>
|
||||
# - dead since 2022/10 - 2023/03
|
||||
|
||||
{ lib, sane-data, ... }:
|
||||
let
|
||||
hourly = { freq = "hourly"; };
|
||||
|
@ -75,7 +80,6 @@ let
|
|||
(fromDb "feeds.simplecast.com/wgl4xEgL" // rat) # Econ Talk
|
||||
(fromDb "feeds.simplecast.com/xKJ93w_w" // uncat) # Atlas Obscura
|
||||
(fromDb "feeds.transistor.fm/acquired" // tech)
|
||||
(fromDb "feeds.twit.tv/floss.xml" // tech)
|
||||
(fromDb "fulltimenix.com" // tech)
|
||||
(fromDb "futureofcoding.org/episodes" // tech)
|
||||
(fromDb "hackerpublicradio.org" // tech)
|
||||
|
@ -99,7 +103,6 @@ let
|
|||
(fromDb "seattlenice.buzzsprout.com" // pol)
|
||||
(fromDb "srslywrong.com" // pol)
|
||||
(fromDb "sharkbytes.transistor.fm" // tech) # Wireshark Podcast o_0
|
||||
(fromDb "sharptech.fm/feed/podcast" // tech)
|
||||
(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)
|
||||
|
@ -132,7 +135,6 @@ let
|
|||
(fromDb "artemis.sh" // tech)
|
||||
(fromDb "ascii.textfiles.com" // tech) # Jason Scott
|
||||
(fromDb "austinvernon.site" // tech)
|
||||
(fromDb "buttondown.email" // tech)
|
||||
(fromDb "ben-evans.com/benedictevans" // pol)
|
||||
(fromDb "bitbashing.io" // tech)
|
||||
(fromDb "bitsaboutmoney.com" // uncat)
|
||||
|
@ -194,7 +196,6 @@ let
|
|||
(fromDb "willow.phantoma.online") # wizard@xyzzy.link
|
||||
(fromDb "xn--gckvb8fzb.com" // tech)
|
||||
(fromDb "xorvoid.com" // tech)
|
||||
(fromDb "www.thebignewsletter.com" // pol)
|
||||
(mkSubstack "astralcodexten" // rat // daily) # Scott Alexander
|
||||
(mkSubstack "eliqian" // rat // weekly)
|
||||
(mkSubstack "oversharing" // pol // daily)
|
||||
|
|
|
@ -62,7 +62,6 @@
|
|||
sane.ids.clightning.gid = 2419;
|
||||
sane.ids.nix-serve.uid = 2420;
|
||||
sane.ids.nix-serve.gid = 2420;
|
||||
sane.ids.plugdev.gid = 2421;
|
||||
|
||||
sane.ids.colin.uid = 1000;
|
||||
sane.ids.guest.uid = 1100;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
# - each namespace may use a different /etc/resolv.conf to specify different DNS servers
|
||||
# - 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.
|
||||
# - this is fixed by either removing `/var/run/nscd/socket` from the namespace, or disabling nscd altogether.
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ config, lib, ... }:
|
||||
lib.mkMerge [
|
||||
{
|
||||
sane.services.trust-dns.enable = lib.mkDefault config.sane.services.trust-dns.asSystemResolver;
|
||||
|
@ -59,35 +59,15 @@ lib.mkMerge [
|
|||
# in the netns and we query upstream DNS more often than needed. hm.
|
||||
# services.nscd.enableNsncd = true;
|
||||
|
||||
# disabling nscd LOSES US SOME FUNCTIONALITY. in particular, only the glibc-builtin modules are accessible via /etc/resolv.conf (er, did i mean /etc/nsswitch.conf?).
|
||||
# disabling nscd LOSES US SOME FUNCTIONALITY. in particular, only the glibc-builtin modules are accessible via /etc/resolv.conf.
|
||||
# - dns: glibc-bultin
|
||||
# - files: glibc-builtin
|
||||
# - myhostname: systemd
|
||||
# - mymachines: systemd
|
||||
# - resolve: systemd
|
||||
# in practice, i see no difference with nscd disabled.
|
||||
# - the exception is when the system dns resolver doesn't do everything.
|
||||
# for example, systemd-resolved does mDNS. hickory-dns does not. a hickory-dns system won't be mDNS-capable.
|
||||
# disabling nscd VASTLY simplifies netns and process isolation. see explainer at top of file.
|
||||
services.nscd.enable = false;
|
||||
# system.nssModules = lib.mkForce [];
|
||||
sane.silencedAssertions = [''.*Loading NSS modules from system.nssModules.*requires services.nscd.enable being set to true.*''];
|
||||
# add NSS modules into their own subdirectory.
|
||||
# then i can add just the NSS modules library path to the global LD_LIBRARY_PATH, rather than ALL of /run/current-system/sw/lib.
|
||||
# TODO: i'm doing this so as to achieve mdns DNS resolution (avahi). it would be better to just have trust-dns delegate .local to avahi
|
||||
# (except avahi doesn't act as a local resolver over DNS protocol -- only dbus).
|
||||
environment.systemPackages = [(pkgs.symlinkJoin {
|
||||
name = "nss-modules";
|
||||
paths = config.system.nssModules.list;
|
||||
postBuild = ''
|
||||
mkdir nss
|
||||
mv $out/lib/libnss_* nss
|
||||
rm -rf $out
|
||||
mkdir -p $out/lib
|
||||
mv nss $out/lib
|
||||
'';
|
||||
})];
|
||||
environment.variables.LD_LIBRARY_PATH = [ "/run/current-system/sw/lib/nss" ];
|
||||
systemd.globalEnvironment.LD_LIBRARY_PATH = "/run/current-system/sw/lib/nss"; #< specifically for `geoclue.service`
|
||||
system.nssModules = lib.mkForce [];
|
||||
}
|
||||
]
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
# 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) {
|
||||
source = pkgs.sane-nix-files;
|
||||
source = ../..;
|
||||
};
|
||||
environment.etc."nix/registry.json" = lib.mkIf (config.sane.maxBuildCost < 3) {
|
||||
enable = false;
|
||||
|
|
|
@ -151,7 +151,7 @@ in
|
|||
# "ponymix"
|
||||
"pulsemixer"
|
||||
"python3-repl"
|
||||
# "python3.pkgs.eyeD3" # music tagging
|
||||
# "python3Packages.eyeD3" # music tagging
|
||||
"ripgrep" # needed as a user package so that its user-level config file can be installed
|
||||
"rsync"
|
||||
"sane-scripts.bittorrent"
|
||||
|
@ -175,12 +175,8 @@ in
|
|||
# "gh" # MS GitHub cli
|
||||
"nix-index"
|
||||
"nixpkgs-review"
|
||||
"qmk-udev-rules"
|
||||
"sane-scripts.dev"
|
||||
"sequoia"
|
||||
# "via"
|
||||
"wally-cli"
|
||||
# "zsa-udev-rules"
|
||||
];
|
||||
|
||||
consoleMediaUtils = declPackageSet [
|
||||
|
@ -280,16 +276,16 @@ in
|
|||
# "gnome.cheese"
|
||||
# "gnome-feeds" # RSS reader (with claimed mobile support)
|
||||
# "gnome.file-roller"
|
||||
"geary" # adaptive e-mail client; uses webkitgtk 4.1
|
||||
"gnome-calculator"
|
||||
"gnome-calendar"
|
||||
"gnome.geary" # adaptive e-mail client; uses webkitgtk 4.1
|
||||
"gnome.gnome-calculator"
|
||||
"gnome.gnome-calendar"
|
||||
"gnome.gnome-clocks"
|
||||
"gnome.gnome-maps"
|
||||
# "gnome-podcasts"
|
||||
# "gnome.gnome-system-monitor"
|
||||
# "gnome.gnome-terminal" # works on phosh
|
||||
"gnome.gnome-weather"
|
||||
# "seahorse" # keyring/secret manager
|
||||
# "gnome.seahorse" # keyring/secret manager
|
||||
"gnome-frog" # OCR/QR decoder
|
||||
"gpodder"
|
||||
"gst-device-monitor" # for debugging audio/video
|
||||
|
@ -360,14 +356,13 @@ in
|
|||
"gimp" # broken on phosh
|
||||
# "gnome.dconf-editor"
|
||||
# "gnome.file-roller"
|
||||
"gnome-disk-utility"
|
||||
"nautilus" # file browser
|
||||
"gnome.gnome-disk-utility"
|
||||
"gnome.nautilus" # file browser
|
||||
# "gnome.totem" # video player, supposedly supports UPnP
|
||||
# "handbrake" #< TODO: fix build
|
||||
"inkscape"
|
||||
# "jellyfin-media-player"
|
||||
"kdenlive"
|
||||
# "keymapp"
|
||||
# "kid3" # audio tagging
|
||||
"krita"
|
||||
"libreoffice" # TODO: replace with an office suite that uses saner packaging?
|
||||
|
@ -579,6 +574,10 @@ in
|
|||
gawk.sandbox.wrapperType = "inplace"; # /share/gawk libraries refer to /libexec
|
||||
gawk.sandbox.autodetectCliPaths = "existingFile";
|
||||
|
||||
gdb.sandbox.enable = false; # gdb doesn't sandbox well. i don't know how you could.
|
||||
# gdb.sandbox.method = "landlock"; # permission denied when trying to attach, even as root
|
||||
gdb.sandbox.autodetectCliPaths = true;
|
||||
|
||||
geoclue2-with-demo-agent = {};
|
||||
|
||||
# MS GitHub stores auth token in .config
|
||||
|
@ -605,32 +604,32 @@ in
|
|||
"/tmp" # "Cannot open display:" if it can't mount /tmp 👀
|
||||
];
|
||||
|
||||
gnome-calculator.buildCost = 1;
|
||||
gnome-calculator.sandbox.method = "bwrap";
|
||||
gnome-calculator.sandbox.whitelistWayland = true;
|
||||
"gnome.gnome-calculator".buildCost = 1;
|
||||
"gnome.gnome-calculator".sandbox.method = "bwrap";
|
||||
"gnome.gnome-calculator".sandbox.whitelistWayland = true;
|
||||
|
||||
gnome-calendar.buildCost = 1;
|
||||
"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-calendar.sandbox.method = "bwrap";
|
||||
gnome-calendar.sandbox.whitelistWayland = true;
|
||||
"gnome.gnome-calendar".sandbox.method = "bwrap";
|
||||
"gnome.gnome-calendar".sandbox.whitelistWayland = true;
|
||||
|
||||
# gnome-disks
|
||||
gnome-disk-utility.buildCost = 1;
|
||||
gnome-disk-utility.sandbox.method = "bwrap";
|
||||
gnome-disk-utility.sandbox.whitelistDbus = [ "system" ];
|
||||
gnome-disk-utility.sandbox.whitelistWayland = true;
|
||||
gnome-disk-utility.sandbox.extraHomePaths = [
|
||||
"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.
|
||||
seahorse.buildCost = 1;
|
||||
# N.B. it can lso manage ~/.ssh keys, but i explicitly don't add those to the sandbox for now.
|
||||
seahorse.sandbox.method = "bwrap";
|
||||
seahorse.sandbox.whitelistDbus = [ "user" ];
|
||||
seahorse.sandbox.whitelistWayland = true;
|
||||
"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";
|
||||
|
@ -1024,9 +1023,7 @@ in
|
|||
"Music"
|
||||
"tmp"
|
||||
"use"
|
||||
".config/dconf"
|
||||
];
|
||||
soundconverter.sandbox.whitelistDbus = [ "user" ]; # for dconf
|
||||
soundconverter.sandbox.extraPaths = [
|
||||
"/mnt/servo/media/Music"
|
||||
"/mnt/servo/media/games"
|
||||
|
@ -1184,12 +1181,13 @@ in
|
|||
];
|
||||
};
|
||||
|
||||
hardware.graphics = lib.mkIf config.sane.programs.guiApps.enabled ({
|
||||
hardware.opengl = lib.mkIf config.sane.programs.guiApps.enabled ({
|
||||
enable = true;
|
||||
driSupport = lib.mkDefault true;
|
||||
} // (lib.optionalAttrs pkgs.stdenv.isx86_64 {
|
||||
# for 32 bit applications
|
||||
# upstream nixpkgs forbids setting enable32Bit unless specifically x86_64 (so aarch64 isn't allowed)
|
||||
enable32Bit = lib.mkDefault true;
|
||||
# upstream nixpkgs forbids setting driSupport32Bit unless specifically x86_64 (so aarch64 isn't allowed)
|
||||
driSupport32Bit = lib.mkDefault true;
|
||||
}));
|
||||
|
||||
system.activationScripts.notifyActive = lib.mkIf config.sane.programs.guiApps.enabled {
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
# Avahi zeroconf (mDNS) implementation.
|
||||
# runs as systemd `avahi-daemon.service`
|
||||
#
|
||||
# - <https://avahi.org/>
|
||||
# - code: <https://github.com/avahi/avahi>
|
||||
# - IRC: #avahi on irc.libera.chat
|
||||
#
|
||||
# - `avahi-browse --help` for usage
|
||||
# - `man avahi-daemon.conf`
|
||||
# - `LD_LIBRARY_PATH=/nix/store/ngwj3jqmxh8k4qji2z0lj7y1f8vzqrn2-nss-mdns-0.15.1/lib getent hosts desko.local`
|
||||
# nss-mdns goes through avahi-daemon, so there IS caching here
|
||||
#
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
sane.programs.avahi = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistDbus = [ "system" ];
|
||||
sandbox.net = "all"; #< otherwise it will show 'null' in place of each interface name.
|
||||
};
|
||||
services.avahi = lib.mkIf config.sane.programs.avahi.enabled {
|
||||
enable = true;
|
||||
package = config.sane.programs.avahi.package;
|
||||
publish.enable = true;
|
||||
publish.userServices = true;
|
||||
nssmdns4 = true;
|
||||
nssmdns6 = true;
|
||||
# reflector = true;
|
||||
allowInterfaces = [
|
||||
# particularly, the default config disallows loopback, which is kinda fucking retarded, right?
|
||||
"ens1" #< servo
|
||||
"enp5s0" #< desko
|
||||
"lo"
|
||||
"wg-home"
|
||||
"wlan0" #< moby
|
||||
"wlp3s0" #< lappy
|
||||
"wlp4s0" #< desko
|
||||
];
|
||||
};
|
||||
}
|
|
@ -95,7 +95,7 @@ in
|
|||
|
||||
packageUnwrapped = pkgs.bemenu.overrideAttrs (upstream: {
|
||||
nativeBuildInputs = (upstream.nativeBuildInputs or []) ++ [
|
||||
pkgs.makeBinaryWrapper
|
||||
pkgs.makeWrapper
|
||||
];
|
||||
# can alternatively be specified as CLI flags
|
||||
postInstall = (upstream.postInstall or "") + ''
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p blast-ugjka -p python3
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p blast-ugjka
|
||||
# vim: set filetype=python :
|
||||
|
||||
import logging
|
||||
|
|
|
@ -31,7 +31,7 @@ in
|
|||
|
||||
sane.programs.blast-to-default = {
|
||||
# helper to deal with blast's interactive CLI
|
||||
packageUnwrapped = pkgs.static-nix-shell.mkPython3 {
|
||||
packageUnwrapped = pkgs.static-nix-shell.mkPython3Bin {
|
||||
pname = "blast-to-default";
|
||||
pkgs = [ "blast-ugjka" ];
|
||||
srcRoot = ./.;
|
||||
|
|
|
@ -111,7 +111,7 @@ in
|
|||
'';
|
||||
});
|
||||
|
||||
fs.".config/bonsai/bonsai_tree.json".symlink.target = pkgs.writers.writeJSON "bonsai_tree.json" cfg.config.transitions;
|
||||
fs.".config/bonsai/bonsai_tree.json".symlink.text = builtins.toJSON cfg.config.transitions;
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.extraRuntimePaths = [
|
||||
|
|
|
@ -24,7 +24,7 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
packageUnwrapped = pkgs.rmDbusServicesInPlace (pkgs.calls.overrideAttrs (upstream: {
|
||||
packageUnwrapped = pkgs.calls.overrideAttrs (upstream: {
|
||||
patches = (upstream.patches or []) ++ [
|
||||
(pkgs.fetchpatch {
|
||||
# usability improvement... if the UI is visible, then i can receive calls. otherwise, i can't!
|
||||
|
@ -33,7 +33,7 @@ in
|
|||
hash = "sha256-NoVQV2TlkCcsBt0uwSyK82hBKySUW4pADrJVfLFvWgU=";
|
||||
})
|
||||
];
|
||||
}));
|
||||
});
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.net = "clearnet";
|
||||
|
|
|
@ -66,7 +66,6 @@ end
|
|||
if vars.percent ~= nil then
|
||||
bat_args = bat_args .. " --percent-suffix '" .. vars.percent .. "'"
|
||||
end
|
||||
bat_args = bat_args .. " {bat}"
|
||||
|
||||
-- N.B.: `[[ <text> ]]` is Lua's multiline string literal
|
||||
conky.text = [[
|
||||
|
@ -74,8 +73,8 @@ ${color1}${shadecolor 707070}${font sans-serif:size=50:style=Bold}${alignc}${exe
|
|||
${color2}${shadecolor a4d7d0}${font sans-serif:size=20}${alignc}${exec date +"%a %d %b"}${font}
|
||||
|
||||
|
||||
${color1}${shadecolor}${font sans-serif:size=22:style=Bold}${alignc}${execp sane-sysload ]] .. bat_args .. [[ }${font}
|
||||
${color1}${shadecolor}${font sans-serif:size=20:style=Bold}${alignc}${texeci 600 timeout 20 sane-weather }${font}
|
||||
${color1}${shadecolor}${font sans-serif:size=22:style=Bold}${alignc}${execp @bat@ ]] .. bat_args .. [[ }${font}
|
||||
${color1}${shadecolor}${font sans-serif:size=20:style=Bold}${alignc}${texeci 600 @weather@ }${font}
|
||||
|
||||
|
||||
${color2}${shadecolor a4d7d0}${font sans-serif:size=16}${alignc}⇅ ${downspeedf wlan0}]] .. vars.kBps .. [[${font}
|
||||
|
|
|
@ -1,22 +1,38 @@
|
|||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs.sane-battery-estimate = {
|
||||
packageUnwrapped = pkgs.static-nix-shell.mkBash {
|
||||
pname = "sane-battery-estimate";
|
||||
srcRoot = ./.;
|
||||
};
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.extraPaths = [
|
||||
"/sys/class/power_supply"
|
||||
"/sys/devices"
|
||||
];
|
||||
};
|
||||
|
||||
sane.programs.conky = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.net = "clearnet"; #< for the scripts it calls (weather)
|
||||
sandbox.extraPaths = [
|
||||
"/sys/class/power_supply"
|
||||
"/sys/devices" # needed by sane-sysload
|
||||
"/sys/devices" # needed by battery_estimate
|
||||
# "/sys/devices/cpu"
|
||||
# "/sys/devices/system"
|
||||
];
|
||||
sandbox.whitelistWayland = true;
|
||||
|
||||
suggestedPrograms = [
|
||||
"sane-sysload"
|
||||
"sane-battery-estimate"
|
||||
"sane-weather"
|
||||
];
|
||||
|
||||
fs.".config/conky/conky.conf".symlink.target = ./conky.conf;
|
||||
fs.".config/conky/conky.conf".symlink.target = pkgs.substituteAll {
|
||||
src = ./conky.conf;
|
||||
bat = "sane-battery-estimate";
|
||||
weather = "timeout 20 sane-weather";
|
||||
};
|
||||
|
||||
services.conky = {
|
||||
description = "conky dynamic desktop background";
|
||||
|
|
183
hosts/common/programs/conky/sane-battery-estimate
Executable file
183
hosts/common/programs/conky/sane-battery-estimate
Executable file
|
@ -0,0 +1,183 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash
|
||||
|
||||
usage() {
|
||||
echo "usage: battery_estimate [options...]"
|
||||
echo
|
||||
echo "pretty-prints a battery estimate (icon to indicate state, and a duration estimate)"
|
||||
echo
|
||||
echo "options:"
|
||||
echo " --debug: output additional information, to stderr"
|
||||
echo " --minute-suffix <string>: use the provided string as a minutes suffix"
|
||||
echo " --hour-suffix <string>: use the provided string as an hours suffix"
|
||||
echo " --icon-suffix <string>: use the provided string as an icon suffix"
|
||||
echo " --percent-suffix <string>: use the provided string when displaying percents"
|
||||
}
|
||||
|
||||
# these icons may only render in nerdfonts
|
||||
icon_bat_chg=("" "" "" "")
|
||||
icon_bat_dis=("" "" "" "")
|
||||
suffix_icon=" " # thin space
|
||||
suffix_percent="%"
|
||||
# suffix_icon=" "
|
||||
|
||||
# render time like: 2ʰ08ᵐ
|
||||
# unicode sub/super-scripts: <https://en.wikipedia.org/wiki/Unicode_subscripts_and_superscripts>
|
||||
# symbol_hr="ʰ"
|
||||
# symbol_min="ᵐ"
|
||||
|
||||
# render time like: 2ₕ08ₘ
|
||||
# symbol_hr="ₕ"
|
||||
# symbol_min="ₘ"
|
||||
|
||||
# render time like: 2h08m
|
||||
# symbol_hr="h"
|
||||
# symbol_min="m"
|
||||
|
||||
# render time like: 2:08
|
||||
# symbol_hr=":"
|
||||
# symbol_min=
|
||||
|
||||
# render time like: 2꞉08⧗
|
||||
symbol_hr="꞉"
|
||||
symbol_min="⧗"
|
||||
# variants:
|
||||
# symbol_hr=":"
|
||||
# symbol_min="⧖"
|
||||
# symbol_min="⌛"
|
||||
|
||||
# render time like: 2'08"
|
||||
# symbol_hr="'"
|
||||
# symbol_min='"'
|
||||
|
||||
log() {
|
||||
if [ "$BATTERY_ESTIMATE_DEBUG" = "1" ]; then
|
||||
printf "$@" >&2
|
||||
echo >&2
|
||||
fi
|
||||
}
|
||||
|
||||
render_icon() {
|
||||
# args:
|
||||
# 1: "chg" or "dis"
|
||||
# 2: current battery percentage
|
||||
level=$(($2 / 25))
|
||||
level=$(($level > 3 ? 3 : $level))
|
||||
level=$(($level < 0 ? 0 : $level))
|
||||
log "icon: %s %d" "$1" "$level"
|
||||
if [ "$1" = "dis" ]; then
|
||||
printf "%s" "${icon_bat_dis[$level]}"
|
||||
elif [ "$1" = "chg" ]; then
|
||||
printf "%s" "${icon_bat_chg[$level]}"
|
||||
fi
|
||||
}
|
||||
|
||||
try_path() {
|
||||
# assigns output variables:
|
||||
# - perc, perc_from_full (0-100)
|
||||
# - full, rate (pos means charging)
|
||||
if [ -f "$1/capacity" ]; then
|
||||
log "perc, perc_from_full from %s" "$1/capacity"
|
||||
perc=$(cat "$1/capacity")
|
||||
perc_from_full=$((100 - $perc))
|
||||
fi
|
||||
|
||||
if [ -f "$1/charge_full_design" ] && [ -f "$1/current_now" ]; then
|
||||
log "full, rate from %s and %s" "$1/charge_full_design" "$1/current_now"
|
||||
# current is positive when charging
|
||||
full=$(cat "$1/charge_full_design")
|
||||
rate=$(cat "$1/current_now")
|
||||
elif [ -f "$1/energy_full" ] && [ -f "$1/power_now" ]; then
|
||||
log "full, rate from %s and %s" "$1/energy_full" "$1/power_now"
|
||||
# power_now is positive when discharging
|
||||
full=$(cat "$1/energy_full")
|
||||
rate=-$(cat "$1/power_now")
|
||||
elif [ -f "$1/energy_full" ] && [ -f "$1/energy_now" ]; then
|
||||
log "full, rate from %s and %s" "$1/energy_full" "$1/energy_now"
|
||||
log " this is a compatibility path for legacy Thinkpad batteries which do not populate the 'power_now' field, and incorrectly populate 'energy_now' with power info"
|
||||
# energy_now is positive when discharging
|
||||
full=$(cat "$1/energy_full")
|
||||
rate=-$(cat "$1/energy_now")
|
||||
fi
|
||||
}
|
||||
|
||||
try_all_paths() {
|
||||
try_path "/sys/class/power_supply/axp20x-battery" # Pinephone
|
||||
try_path "/sys/class/power_supply/BAT0" # Thinkpad
|
||||
log "perc: %d, perc_from_full: %d" "$perc" "$perc_from_full"
|
||||
log "full: %f, rate: %f" "$full" "$rate"
|
||||
log " rate > 0 means charging, else discharging"
|
||||
}
|
||||
|
||||
fmt_minutes() {
|
||||
# args:
|
||||
# 1: icon to render
|
||||
# 2: string to show if charge/discharge time is indefinite
|
||||
# 3: minutes to stable state (i.e. to full charge or full discharge)
|
||||
# - we work in minutes instead of hours for precision: bash math is integer-only
|
||||
log "charge/discharge time: %f min" "$3"
|
||||
# args: <battery symbol> <text if ludicrous estimate> <estimated minutes to full/empty>
|
||||
if [ -n "$3" ] && [ "$3" -lt 1440 ]; then
|
||||
hr=$(($3 / 60))
|
||||
hr_in_min=$(($hr * 60))
|
||||
min=$(($3 - $hr_in_min))
|
||||
printf "%s%s%d%s%02d%s" "$1" "$suffix_icon" "$hr" "$symbol_hr" "$min" "$symbol_min"
|
||||
else
|
||||
log "charge/discharge duration > 1d"
|
||||
printf "%s%s%s" "$1" "$suffix_icon" "$2" # more than 1d
|
||||
fi
|
||||
}
|
||||
|
||||
pretty_output() {
|
||||
if [ -n "$perc" ]; then
|
||||
duration=""
|
||||
if [ "$rate" -gt 0 ]; then
|
||||
log "charging"
|
||||
icon="$(render_icon chg $perc)"
|
||||
duration="$(($full * 60 * $perc_from_full / (100 * $rate)))"
|
||||
else
|
||||
log "discharging"
|
||||
icon="$(render_icon dis $perc)"
|
||||
if [ "$rate" -lt 0 ]; then
|
||||
duration="$(($full * 60 * $perc / (-100 * $rate)))"
|
||||
fi
|
||||
fi
|
||||
fmt_minutes "$icon" "$perc$suffix_percent" "$duration"
|
||||
fi
|
||||
}
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
"--debug")
|
||||
shift
|
||||
BATTERY_ESTIMATE_DEBUG=1
|
||||
;;
|
||||
"--icon-suffix")
|
||||
shift
|
||||
suffix_icon="$1"
|
||||
shift
|
||||
;;
|
||||
"--hour-suffix")
|
||||
shift
|
||||
symbol_hr="$1"
|
||||
shift
|
||||
;;
|
||||
"--minute-suffix")
|
||||
shift
|
||||
symbol_min="$1"
|
||||
shift
|
||||
;;
|
||||
"--percent-suffix")
|
||||
shift
|
||||
suffix_percent="$1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
try_all_paths
|
||||
pretty_output
|
|
@ -10,7 +10,6 @@
|
|||
./assorted.nix
|
||||
./audacity.nix
|
||||
./ausyscall.nix
|
||||
./avahi.nix
|
||||
./bemenu.nix
|
||||
./blast-ugjka
|
||||
./bonsai.nix
|
||||
|
@ -51,11 +50,8 @@
|
|||
./fwupd.nix
|
||||
./g4music.nix
|
||||
./gajim.nix
|
||||
./gdb.nix
|
||||
./gdbus.nix
|
||||
./geary.nix
|
||||
./geoclue-demo-agent.nix
|
||||
./geoclue2.nix
|
||||
./git.nix
|
||||
./gnome-clocks.nix
|
||||
./gnome-feeds.nix
|
||||
|
@ -64,8 +60,6 @@
|
|||
./gnome-weather.nix
|
||||
./go2tv.nix
|
||||
./gpodder.nix
|
||||
./gpsd.nix
|
||||
./gps-share.nix
|
||||
./grimshot.nix
|
||||
./gst-device-monitor.nix
|
||||
./gthumb.nix
|
||||
|
@ -73,11 +67,9 @@
|
|||
./handbrake.nix
|
||||
./helix.nix
|
||||
./htop
|
||||
./iio-sensor-proxy.nix
|
||||
./imagemagick.nix
|
||||
./jellyfin-media-player.nix
|
||||
./kdenlive.nix
|
||||
./keymapp.nix
|
||||
./komikku.nix
|
||||
./koreader
|
||||
./less.nix
|
||||
|
@ -95,7 +87,6 @@
|
|||
./msmtp.nix
|
||||
./nautilus.nix
|
||||
./neovim.nix
|
||||
./networkmanager_dmenu
|
||||
./newsflash.nix
|
||||
./nheko.nix
|
||||
./nicotine-plus.nix
|
||||
|
@ -103,21 +94,15 @@
|
|||
./nmcli.nix
|
||||
./notejot.nix
|
||||
./ntfy-sh.nix
|
||||
./nwg-panel
|
||||
./objdump.nix
|
||||
./obsidian.nix
|
||||
./offlineimap.nix
|
||||
./ols.nix
|
||||
./open-in-mpv.nix
|
||||
./pactl.nix
|
||||
./pidof.nix
|
||||
./pipewire.nix
|
||||
./pkill.nix
|
||||
./planify.nix
|
||||
./portfolio-filemanager.nix
|
||||
./playerctl.nix
|
||||
./ps.nix
|
||||
./qmk-udev-rules.nix
|
||||
./rhythmbox.nix
|
||||
./ripgrep.nix
|
||||
./rofi
|
||||
|
@ -127,10 +112,8 @@
|
|||
./sane-open.nix
|
||||
./sane-screenshot.nix
|
||||
./sane-scripts.nix
|
||||
./sane-sysload.nix
|
||||
./sane-theme.nix
|
||||
./sanebox.nix
|
||||
./satellite.nix
|
||||
./schlock.nix
|
||||
./seatd.nix
|
||||
./sfeed.nix
|
||||
|
@ -150,17 +133,14 @@
|
|||
./swaylock.nix
|
||||
./swaynotificationcenter
|
||||
./switchboard.nix
|
||||
./syshud.nix
|
||||
./sysvol.nix
|
||||
./tangram.nix
|
||||
./tor-browser.nix
|
||||
./tuba.nix
|
||||
./unl0kr
|
||||
./via.nix
|
||||
./vlc.nix
|
||||
./wally-cli.nix
|
||||
./waybar
|
||||
./waylock.nix
|
||||
./where-am-i.nix
|
||||
./wike.nix
|
||||
./wine.nix
|
||||
./wireplumber.nix
|
||||
|
@ -176,7 +156,6 @@
|
|||
./zeal.nix
|
||||
./zecwallet-lite.nix
|
||||
./zulip.nix
|
||||
./zsa-udev-rules.nix
|
||||
./zsh
|
||||
];
|
||||
|
||||
|
|
|
@ -50,13 +50,32 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
packageUnwrapped = pkgs.dino.override {
|
||||
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";
|
||||
|
|
|
@ -55,7 +55,7 @@ in
|
|||
# - theme-demo
|
||||
# - timeout-completed
|
||||
# - window-close
|
||||
fs.".config/feedbackd/themes/proxied.json".symlink.target = pkgs.writers.writeJSON "proxied.json" {
|
||||
fs.".config/feedbackd/themes/proxied.json".symlink.text = builtins.toJSON {
|
||||
name = "proxied";
|
||||
parent-theme = "default";
|
||||
profiles = [
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs.gdb = {
|
||||
sandbox.enable = false; # gdb doesn't sandbox well. i don't know how you could.
|
||||
# sandbox.method = "landlock"; # permission denied when trying to attach, even as root
|
||||
sandbox.autodetectCliPaths = true;
|
||||
fs.".config/gdb/gdbinit".symlink.text = ''
|
||||
# enable commands like `py-bt`, `py-list`, etc.
|
||||
# for usage, see: <https://wiki.python.org/moin/DebuggingWithGdb>
|
||||
source ${pkgs.python3}/share/gdb/libpython.py
|
||||
'';
|
||||
};
|
||||
}
|
|
@ -5,10 +5,10 @@
|
|||
# <https://gitlab.gnome.org/GNOME/geary/-/issues/1212>
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.sane.programs."geary";
|
||||
cfg = config.sane.programs."gnome.geary";
|
||||
in
|
||||
{
|
||||
sane.programs."geary" = {
|
||||
sane.programs."gnome.geary" = {
|
||||
configOption = with lib; mkOption {
|
||||
default = {};
|
||||
type = types.submodule {
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
{ config, pkgs, ... }:
|
||||
{
|
||||
sane.programs.geoclue-demo-agent = {
|
||||
packageUnwrapped = pkgs.linkFarm "geoclue-demo-agent" [{
|
||||
# bring the demo agent into a `bin/` directory so it can be invokable via PATH
|
||||
name = "bin/geoclue-demo-agent";
|
||||
path = "${config.sane.programs.geoclue2.packageUnwrapped}/libexec/geoclue-2.0/demos/agent";
|
||||
}];
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistDbus = [
|
||||
"system"
|
||||
];
|
||||
|
||||
services.geoclue-agent = {
|
||||
description = "geoclue 'demo' agent";
|
||||
# XXX: i don't actually understand how this works: upstream dbus rules would appear to restrict
|
||||
# the dbus owner to just root/geoclue, but we're neither and this still works (and breaks if i remove the agent service!)
|
||||
command = "geoclue-demo-agent";
|
||||
partOf = [ "graphical-session" ];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
# geoclue location services daemon.
|
||||
#
|
||||
# SUPPORT:
|
||||
# - irc: #gnome-maps on irc.gimp.org
|
||||
# - Matrix: #gnome-maps:gnome.org (unclear if bridged to IRC)
|
||||
# - forums: <https://discourse.gnome.org/c/platform>
|
||||
# - git: <https://gitlab.freedesktop.org/geoclue/geoclue/>
|
||||
# - D-Bus API docs: <https://www.freedesktop.org/software/geoclue/docs/>
|
||||
#
|
||||
# HOW TO TEST:
|
||||
# - just invoke `where-am-i`: it should output the current latitude/longitude.
|
||||
## more manual testing:
|
||||
# - build `geoclue2-with-demo-agent`
|
||||
# - run the service: `systemctl start geoclue` or "${geoclue2-with-demo-agent}/libexec/geoclue"
|
||||
# - run "${geoclue2-with-demo-agent}/libexec/geoclue-2.0/demos/agent"
|
||||
# - keep this running in the background
|
||||
# - run "${geoclue2-with-demo-agent}/libexec/geoclue-2.0/demos/where-am-i"
|
||||
#
|
||||
# DATA FLOW:
|
||||
# - geoclue2 does http calls into local `ols`, which either hits the local disk or queries https://wigle.net.
|
||||
# - geoclue users like gnome-maps somehow depend on an "agent",
|
||||
# a user service which launches the geoclue system service on-demand (via dbus activation).
|
||||
#
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.geoclue2;
|
||||
in
|
||||
{
|
||||
sane.programs.geoclue2 = {
|
||||
# packageUnwrapped = pkgs.rmDbusServices pkgs.geoclue2;
|
||||
# packageUnwrapped = pkgs.geoclue2.override { withDemoAgent = true; };
|
||||
packageUnwrapped = pkgs.geoclue2-with-demo-agent;
|
||||
suggestedPrograms = [
|
||||
"avahi" #< to discover LAN gps devices
|
||||
"geoclue-demo-agent"
|
||||
"gps-share"
|
||||
"iio-sensor-proxy"
|
||||
"ols" #< WiFi SSID -> lat/long lookups
|
||||
"satellite" #< graphical view into GPS fix data
|
||||
"where-am-i" #< handy debugging/testing tool
|
||||
];
|
||||
};
|
||||
|
||||
# sane.programs.geoclue2.enableFor.system = lib.mkIf (builtins.any (en: en) (builtins.attrValues cfg.enableFor.user)) true;
|
||||
|
||||
services.geoclue2 = lib.mkIf cfg.enabled {
|
||||
enable = true;
|
||||
geoProviderUrl = "http://127.0.0.1:8088/v1/geolocate"; #< ols
|
||||
# XXX(2024/06/25): when Geoclue uses ModemManager's GPS API, it wants to enable GPS
|
||||
# tracking at the start, and disable it at the end. that causes tracking to be lost, regularly.
|
||||
# this is not optional behavior: if Geoclue fails to control modem manager (because of a polkit policy, say),
|
||||
# then it won't even try to read the data from modem manager.
|
||||
# SOLUTION: tell Geoclue to get GPS from gps-share ("enableNmea", i.e. `network-nmea.enable`)
|
||||
# and NOT from modem manager.
|
||||
enableModemGPS = false;
|
||||
enableNmea = true;
|
||||
};
|
||||
systemd.user.services = lib.mkIf cfg.enabled {
|
||||
# nixos services.geoclue2 runs the agent as a user service by default, but i don't use systemd so that doesn't work.
|
||||
# i manage the agent myself, in sane.programs.geoclue-demo-agent.
|
||||
geoclue-agent.enable = false;
|
||||
};
|
||||
}
|
|
@ -40,7 +40,6 @@ in
|
|||
alias.amend = "commit --amend --no-edit";
|
||||
alias.br = "branch";
|
||||
alias.co = "checkout";
|
||||
alias.com = "commit";
|
||||
alias.cp = "cherry-pick";
|
||||
alias.d = "difftool";
|
||||
alias.dif = "diff"; # common typo
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
# gnome feeds RSS viewer
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
{ config, lib, sane-lib, ... }:
|
||||
|
||||
let
|
||||
feeds = sane-lib.feeds;
|
||||
all-feeds = config.sane.feeds;
|
||||
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
|
||||
in {
|
||||
sane.programs.gnome-feeds.fs.".config/org.gabmus.gfeeds.json".symlink.target = pkgs.writers.writeJSON "org.gabmus.gfeeds.json" {
|
||||
sane.programs.gnome-feeds.fs.".config/org.gabmus.gfeeds.json".symlink.text = builtins.toJSON {
|
||||
# feed format is a map from URL to a dict,
|
||||
# with dict["tags"] a list of string tags.
|
||||
feeds = sane-lib.mapToAttrs (feed: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ lib, pkgs, ... }:
|
||||
{
|
||||
sane.programs.gnome-keyring = {
|
||||
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome-keyring;
|
||||
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-keyring;
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistDbus = [ "user" ];
|
||||
sandbox.extraRuntimePaths = [
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
# SUPPORT:
|
||||
# - irc: #gnome-maps on irc.gimp.org
|
||||
# - Matrix: #gnome-maps:gnome.org (unclear if bridged to IRC)
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs."gnome.gnome-maps" = {
|
||||
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-maps;
|
||||
suggestedPrograms = [
|
||||
"geoclue2"
|
||||
];
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistDri = true; # for perf
|
||||
sandbox.whitelistDbus = [
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
# gps-share: <https://github.com/zeenix/gps-share>
|
||||
# takes a local GPS device (e.g. /dev/ttyUSB1) and makes it available over TCP/Avahi (multicast DNS).
|
||||
#
|
||||
# common usecases:
|
||||
# 1. make positioning available to any device on a network, even if that device has no local GPS
|
||||
# - e.g. my desktop can use my phone's GPS device, if on the same network.
|
||||
# 2. allow multiple clients to share a GPS device.
|
||||
# GPS devices are serial devices, and so only one process can consume the data at a time.
|
||||
# gps-share can camp the serial device, and then allow *multiple* subscribers
|
||||
# 3. provide a *read-only* API to clients like Geoclue.
|
||||
# that is, expose the GPS device *output* to a client, but don't let the client write to the device (e.g. enable/disable the GPS).
|
||||
# this is the primary function i derive from gps-share
|
||||
#
|
||||
# HOW TO TEST:
|
||||
# - `nc localhost 10110`
|
||||
# should stream GPS NMEA output to the console
|
||||
# - `avahi-browse --resolve _nmea-0183._tcp`: should show hosts on the local network which provide GPS info
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.gps-share;
|
||||
in
|
||||
{
|
||||
sane.programs.gps-share = {
|
||||
configOption = with lib; mkOption {
|
||||
default = {};
|
||||
type = types.submodule {
|
||||
options.device = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
path to GPS device to share.
|
||||
'';
|
||||
example = "/dev/ttyUSB1";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.gps-share = lib.mkIf (cfg.config.device != null) {
|
||||
description = "gps-share: make local GPS serial readings available over Avahi";
|
||||
# usage:
|
||||
# gps-share --no-announce # to disable Avahi
|
||||
# gps-share --no-tcp # only makes sense if using --socket-path
|
||||
# gps-share --network-interface lo # defaults to all interfaces, but firewalling means actually more restrictive
|
||||
# gps-share --socket-path $XDG_RUNTIME_DIR/gps-share/gps-share.sock # share over a unix socket
|
||||
command = "gps-share ${cfg.config.device}";
|
||||
# TODO: this should be `partOf = [ "gps" ]`:
|
||||
# it fails to launch if the NMEA device doesn't yet exist, and so restart loop when modem is not booted
|
||||
dependencyOf = [ "geoclue-agent" ];
|
||||
};
|
||||
};
|
||||
|
||||
# TODO: restrict this to just LAN devices!!
|
||||
networking.firewall.allowedTCPPorts = lib.mkIf cfg.enabled [ 10110 ];
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
# test gpsd with `gpspipe -w -n 10 2> /dev/null | grep -m 1 TPV | jq '.lat, .lon' | tr '\n' ' '`
|
||||
# ^ should return <lat> <long>
|
||||
#
|
||||
# TODO(2024/06/19): nixpkgs' gpsd service isn't sandboxed at ALL. i should sandbox that, or remove this integration.
|
||||
#
|
||||
# pinephone GPS happens in EG25 modem
|
||||
# serial control interface to modem is /dev/ttyUSB2
|
||||
# after enabling GPS, readout is /dev/ttyUSB1
|
||||
#
|
||||
# minimal process to enable modem and GPS:
|
||||
# - `echo 1 > /sys/class/modem-power/modem-power/device/powered`
|
||||
# - `screen /dev/ttyUSB2 115200`
|
||||
# - `AT+QGPSCFG="nmeasrc",1`
|
||||
# - `AT+QGPS=1`
|
||||
# this process is automated by my `eg25-control` program and services (`eg25-control-powered`, `eg25-control-gps`)
|
||||
# - see the `modules/` directory further up this repository.
|
||||
#
|
||||
# now, something like `gpsd` can directly read from /dev/ttyUSB1,
|
||||
# or geoclue can query the GPS directly through modem-manager
|
||||
#
|
||||
# initial GPS fix can take 15+ minutes.
|
||||
# meanwhile, services like eg25-manager or eg25-control-freshen-agps can speed this up by uploading assisted GPS data to the modem.
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.gpsd;
|
||||
in
|
||||
{
|
||||
sane.programs.gpsd = {};
|
||||
services.gpsd = lib.mkIf cfg.enabled {
|
||||
enable = true;
|
||||
devices = [ "/dev/ttyUSB1" ];
|
||||
};
|
||||
}
|
|
@ -20,14 +20,5 @@
|
|||
pkgs.pipewire #< required for Video/Source (video4linux)
|
||||
];
|
||||
});
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.extraPaths = [
|
||||
"/dev" # tried, but failed to narrow this down (moby)
|
||||
"/run/udev/data"
|
||||
"/sys/class/video4linux"
|
||||
"/sys/devices"
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
# chat (Matrix): #iio-sensor-proxy:dylanvanassche.be
|
||||
# src: <https://gitlab.freedesktop.org/hadess/iio-sensor-proxy>
|
||||
# IIO = "Industrial I/O": <https://www.kernel.org/doc/html/v4.12/driver-api/iio/index.html>
|
||||
# iio-sensor-proxy reads IIO data reported by the kernel at /sys/bus/iio/* and makes it available to dbus applications.
|
||||
# this includes:
|
||||
# - ambient light sensor
|
||||
# - compass/magnetometer (LIMITED)
|
||||
# - accelerometer (rotation)
|
||||
#
|
||||
# use:
|
||||
# - show available sensors: `gdbus introspect --system --dest net.hadess.SensorProxy --object-path /net/hadess/SensorProxy`
|
||||
# - read sensors: `sudo -u geoclue monitor-sensor --compass`
|
||||
# - default dbus policy only allows geoclue to use the compass
|
||||
# - `sudo monitor-sensor` for light/rotation
|
||||
#
|
||||
# HARDWARE SUPPORT: PINEPHONE (2024/07/01)
|
||||
# - accelerometer and light sensor seem to work
|
||||
# - magnetometer (af8133j, different but similar to lis3mdl) IS NOT SUPPORTED
|
||||
# - <https://gitlab.freedesktop.org/hadess/iio-sensor-proxy/-/issues/310>
|
||||
# - exists in sysfs and can be viewed with
|
||||
# `cat /sys/devices/platform/soc/1c2b000.i2c/i2c-1/1-001c/iio:device2/in_magn_x_raw`
|
||||
# - nothing in iio-sensor-proxy reads anything related to "magn".
|
||||
# - WIP PR to support magnetometers: <https://gitlab.freedesktop.org/hadess/iio-sensor-proxy/-/merge_requests/316>
|
||||
# - after rebase, it *functions*, but does not scale the readings correctly
|
||||
# heading changes only over the range of 50 - 70 deg.
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.iio-sensor-proxy;
|
||||
in
|
||||
{
|
||||
sane.programs.iio-sensor-proxy = {
|
||||
packageUnwrapped = pkgs.iio-sensor-proxy.overrideAttrs (upstream: {
|
||||
patches = (upstream.patches or []) ++ [
|
||||
(pkgs.fetchpatch {
|
||||
name = "WIP:compass: Add support for polling uncalibrated devices";
|
||||
# url = "https://gitlab.freedesktop.org/hadess/iio-sensor-proxy/-/merge_requests/316.diff";
|
||||
url = "https://git.uninsane.org/colin/iio-sensor-proxy/commit/fd21f1f4bf1eadd603b1f24f628b979691d9cf3b.diff";
|
||||
hash = "sha256-+GoEPby6q+uSkQlZWFWr5ghx3BKBMGk7uv/DDhGnxDk=";
|
||||
})
|
||||
];
|
||||
});
|
||||
enableFor.system = lib.mkIf (builtins.any (en: en) (builtins.attrValues cfg.enableFor.user)) true; #< for dbus/polkit policies
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistDbus = [ "system" ];
|
||||
sandbox.extraPaths = [
|
||||
"/run/udev/data"
|
||||
"/sys/bus"
|
||||
"/sys/devices"
|
||||
];
|
||||
};
|
||||
services.udev.packages = lib.mkIf cfg.enabled [ cfg.package ];
|
||||
# services.dbus.packages = lib.mkIf cfg.enabled [ cfg.package ]; #< for bus ownership policy
|
||||
systemd.packages = lib.mkIf cfg.enabled [ cfg.package ]; #< for iio-sensor-proxy.service
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
# ZSA keyboard (Ergodox, Moonlander, ...) firmware flasher and keymap viewer.
|
||||
# video: <https://www.zsa.io/flash>
|
||||
# displays on launch:
|
||||
# - "Error connecting to the keyboard, make sure the layout flashed on your keyboard was recently compiled with Oryx and that the [Live training] option is toggled on in the advanced settings."
|
||||
{ ... }:
|
||||
{
|
||||
sane.programs.keymapp = {};
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
# - these are stored in `~/.config/koreader/data/dict`
|
||||
# - configure defaults:
|
||||
# - edit keys in ~/.config/koreader/settings.reader.lua
|
||||
# - default font size: `["copt_font_size"] = 30,`
|
||||
# - default font size: `["copt_font_size"] = 28,`
|
||||
# - home dir: `["home_dir"] = "/home/colin/Books",`
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
|
||||
|
|
|
@ -1,18 +1,14 @@
|
|||
# docs: <https://git.sr.ht/~mil/mepo>
|
||||
# irc #mepo:irc.oftc.net
|
||||
#
|
||||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
sane.programs.mepo = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.net = "all"; # for tiles *and* for localhost comm to gpsd
|
||||
sandbox.whitelistDri = true;
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.whitelistDbus = [
|
||||
"system" # system is required for non-portal location services
|
||||
"user" #< not sure if "user" is necessary?
|
||||
];
|
||||
sandbox.usePortal = false; # TODO: set up portal-based location services
|
||||
sandbox.whitelistDbus = [ "user" ]; # for geoclue
|
||||
|
||||
persist.byStore.plaintext = [ ".cache/mepo/tiles" ];
|
||||
# ~/.cache/mepo/savestate has precise coordinates and pins: keep those private
|
||||
|
@ -20,11 +16,11 @@
|
|||
{ type = "file"; path = ".cache/mepo/savestate"; }
|
||||
];
|
||||
|
||||
# enable geoclue2 and gpsd for location data.
|
||||
suggestedPrograms = [
|
||||
"geoclue2"
|
||||
# "gpsd" #< not required, and mepo only uses it if geoclue is unavailable
|
||||
];
|
||||
# give mepo access to gpsd for location data, if that's enabled.
|
||||
# same with geoclue2.
|
||||
suggestedPrograms = lib.optional config.services.gpsd.enable "gpsd"
|
||||
++ lib.optional config.services.geoclue2.enable "geoclue2-with-demo-agent"
|
||||
;
|
||||
};
|
||||
|
||||
# programs.mepo = lib.mkIf config.sane.programs.mepo.enabled {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# alternative to mimeo is jaro: <https://github.com/isamert/jaro>
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
mimeo-open-desktop = pkgs.static-nix-shell.mkPython3 {
|
||||
mimeo-open-desktop = pkgs.static-nix-shell.mkPython3Bin {
|
||||
pname = "mimeo-open-desktop";
|
||||
srcRoot = ./.;
|
||||
pkgs = [ "mimeo" ];
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p mimeo -p python3
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p mimeo
|
||||
|
||||
# TODO: migrate nixpkgs mimeo to be `buildPythonPackage` to make it importable here.
|
||||
# see <doc/languages-frameworks/python.section.md>
|
||||
|
|
|
@ -1,19 +1,3 @@
|
|||
# GPS:
|
||||
# - enable: `mmcli --modem any --location-enable-gps-unmanaged`
|
||||
# - or `mmcli -m any --location-enable-gps-nmea`
|
||||
# - or use `s6-rc start eg25-control-gps`
|
||||
# - verify GPS is enabled: `mmcli --modem any --location-status`
|
||||
# - query GPS coordinates: `mmcli -m any --location-get`
|
||||
# - monitor constellation info: `mmcli -m any --location-monitor`
|
||||
# - i.e. which satellites are in view
|
||||
# - or just `cat /dev/ttyUSB1`
|
||||
#
|
||||
# interactions, warnings:
|
||||
# - Geoclue (`where-am-i`) toggles mmcli GPS on/off every 60s, often resetting it to the "off" state
|
||||
# - see: <https://gitlab.freedesktop.org/geoclue/geoclue/-/issues/180>
|
||||
# - the effect is that GPS data is effectively useless inside apps like gnome-maps
|
||||
# i think the trick is to get "--location-enable-gps-unmanaged" gps working again
|
||||
# or use gnss-share/gpsd (this may be what "unmanaged" means).
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs.mmcli = {
|
||||
|
|
|
@ -241,10 +241,9 @@ in
|
|||
mime.associations."video/webm" = "mpv.desktop";
|
||||
mime.associations."video/x-flv" = "mpv.desktop";
|
||||
mime.associations."video/x-matroska" = "mpv.desktop";
|
||||
mime.urlAssociations."^https?://(m\.)?(www\.)?youtu.be/.+" = "mpv.desktop";
|
||||
mime.urlAssociations."^https?://(m\.)?(www\.)?youtube.com/shorts/.+" = "mpv.desktop";
|
||||
mime.urlAssociations."^https?://(m\.)?(www\.)?youtube.com/v/" = "mpv.desktop";
|
||||
mime.urlAssociations."^https?://(m\.)?(www\.)?youtube.com/watch\?.*v=" = "mpv.desktop";
|
||||
mime.urlAssociations."^https?://(www.)?youtube.com/watch\?.*v=" = "mpv.desktop";
|
||||
mime.urlAssociations."^https?://(www.)?youtube.com/v/" = "mpv.desktop";
|
||||
mime.urlAssociations."^https?://(www.)?youtu.be/.+" = "mpv.desktop";
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs."nautilus" = {
|
||||
sane.programs."gnome.nautilus" = {
|
||||
# some of its dbus services don't even refer to real paths
|
||||
packageUnwrapped = pkgs.rmDbusServicesInPlace (pkgs.nautilus.overrideAttrs (orig: {
|
||||
packageUnwrapped = pkgs.rmDbusServicesInPlace (pkgs.gnome.nautilus.overrideAttrs (orig: {
|
||||
# enable the "Audio and Video Properties" pane. see: <https://nixos.wiki/wiki/Nautilus>
|
||||
buildInputs = orig.buildInputs ++ (with pkgs.gst_all_1; [
|
||||
gst-plugins-good
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
[dmenu]
|
||||
dmenu_command = rofi -dmenu
|
||||
# # Note that dmenu_command can contain arguments as well like:
|
||||
# # `dmenu_command = rofi -dmenu -i -theme nmdm`
|
||||
# # `dmenu_command = rofi -dmenu -width 30 -i`
|
||||
# # `dmenu_command = dmenu -i -l 25 -b -nb #909090 -nf #303030`
|
||||
# # `dmenu_command = fuzzel --dmenu`
|
||||
rofi_highlight = True
|
||||
compact = True
|
||||
# pinentry = <Pinentry command> # (Default: None) e.g. `pinentry-gtk`
|
||||
# wifi_chars = <string of 4 unicode characters representing 1-4 bars strength>
|
||||
wifi_chars = ▂▄▆█
|
||||
# wifi_icons = <characters representing signal strength as an icon>
|
||||
wifi_icons =
|
||||
# format = <Python style format string for the access point entries>
|
||||
# # TODO: replace `{sec}` with a locked/unlocked icon
|
||||
format = {icon} {name} [{signal}%%] [{sec}]
|
||||
# # Available variables are:
|
||||
# # * {name} - Access point name
|
||||
# # * {sec} - Security type
|
||||
# # * {signal} - Signal strength on a scale of 0-100
|
||||
# # * {bars} - Bar-based display of signal strength (see wifi_chars)
|
||||
# # * {icon} - Icon-based display of signal strength (see wifi_icons)
|
||||
# # * {max_len_name} and {max_len_sec} are the maximum lengths of {name} / {sec}
|
||||
# # respectively and may be useful for formatting.
|
||||
# list_saved = <True or False> # (Default: False) list saved connections
|
||||
|
||||
[dmenu_passphrase]
|
||||
# # Uses the -password flag for Rofi, -x for bemenu. For dmenu, sets -nb and
|
||||
# # -nf to the same color or uses -P if the dmenu password patch is applied
|
||||
# # https://tools.suckless.org/dmenu/patches/password/
|
||||
# obscure = True
|
||||
# obscure_color = #222222
|
||||
|
||||
[pinentry]
|
||||
# description = <Pinentry description> (Default: Get network password)
|
||||
# prompt = <Pinentry prompt> (Default: Password:)
|
||||
|
||||
[editor]
|
||||
# terminal = <name of terminal program>
|
||||
# gui_if_available = <True or False> (Default: True)
|
||||
|
||||
[nmdm]
|
||||
# rescan_delay = <seconds> # (seconds to wait after a wifi rescan before redisplaying the results)
|
|
@ -1,21 +0,0 @@
|
|||
# source: <https://github.com/firecat53/networkmanager-dmenu>
|
||||
{ ... }:
|
||||
{
|
||||
sane.programs.networkmanager_dmenu = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.isolatePids = false; #< so it can know that NetworkManager really is running... (?)
|
||||
sandbox.whitelistDbus = [
|
||||
"system"
|
||||
];
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.extraHomePaths = [
|
||||
".cache/rofi"
|
||||
".config/rofi"
|
||||
];
|
||||
suggestedPrograms = [
|
||||
"pidof"
|
||||
];
|
||||
|
||||
fs.".config/networkmanager-dmenu/config.ini".symlink.target = ./config.ini;
|
||||
};
|
||||
}
|
|
@ -2,9 +2,6 @@
|
|||
{
|
||||
sane.programs.nmcli = {
|
||||
packageUnwrapped = pkgs.networkmanager-split.nmcli;
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistDbus = [
|
||||
"system"
|
||||
];
|
||||
# TODO: sandbox
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"restart-on-display": true,
|
||||
"restart-delay": 500,
|
||||
"run-through-compositor": false,
|
||||
"processes-background-only": true,
|
||||
"processes-own-only": true
|
||||
}
|
|
@ -1,190 +0,0 @@
|
|||
# TODO:
|
||||
# - try this PR to get custom workspace names to work:
|
||||
# - <https://github.com/nwg-piotr/nwg-panel/pull/191>
|
||||
# - add network/bluetooth indicator
|
||||
# - <https://github.com/nwg-piotr/nwg-panel/issues/269>
|
||||
# - add CPU/meminfo executor
|
||||
# - use sane-sysload
|
||||
{
|
||||
controlsSettingsComponents,
|
||||
height,
|
||||
locker,
|
||||
modulesRight,
|
||||
playerctlChars,
|
||||
mediaPrevNext,
|
||||
windowIcon,
|
||||
windowTitle,
|
||||
workspaceHideEmpty,
|
||||
workspaceNumbers,
|
||||
}:
|
||||
[
|
||||
{
|
||||
controls = "right";
|
||||
css-name = "panel-top";
|
||||
exclusive-zone = true;
|
||||
height = height;
|
||||
homogeneous = false; #< homogenous=false means to not force modules-{left,center,right} to an inflexible 33%/33%/33% real-estate split.
|
||||
icons = "light";
|
||||
items-padding = 0;
|
||||
layer = "bottom";
|
||||
margin-bottom = 0;
|
||||
margin-top = 0;
|
||||
menu-start = "off";
|
||||
name = "panel-top";
|
||||
# output = "All" => display the bar on every output.
|
||||
# - documented: <https://github.com/nwg-piotr/nwg-panel/issues/48>
|
||||
# alternatively, i could declare one bar per display,
|
||||
# and then customize it so that the external display(s) render a less noisy bar.
|
||||
# this will be easier once this is addressed: <https://github.com/nwg-piotr/nwg-panel/issues/215>
|
||||
output = "All";
|
||||
padding-horizontal = 0;
|
||||
padding-vertical = 0;
|
||||
position = "top";
|
||||
sigrt = 64;
|
||||
spacing = 0;
|
||||
start-hidden = false;
|
||||
use-sigrt = false;
|
||||
width = "auto";
|
||||
|
||||
modules-left = [
|
||||
"sway-workspaces"
|
||||
];
|
||||
modules-center = [
|
||||
"clock"
|
||||
];
|
||||
modules-right = modulesRight;
|
||||
|
||||
clock = {
|
||||
angle = 0.0;
|
||||
calendar-css-name = "calendar-window";
|
||||
calendar-icon-size = 24;
|
||||
calendar-interval = 60;
|
||||
calendar-margin-horizontal = 0;
|
||||
calendar-margin-vertical = 0;
|
||||
calendar-on = true;
|
||||
calendar-path = "";
|
||||
calendar-placement = "top";
|
||||
css-name = "clock";
|
||||
format = "%H:%M";
|
||||
interval = 30;
|
||||
on-left-click = "";
|
||||
on-middle-click = "";
|
||||
on-right-click = "";
|
||||
on-scroll-down = "";
|
||||
on-scroll-up = "";
|
||||
root-css-name = "root-clock";
|
||||
tooltip-date-format = true;
|
||||
tooltip-text = "%a; %d %b %H:%M:%S";
|
||||
};
|
||||
controls-settings = {
|
||||
battery-low-interval = 4; #< notify every N minutes when battery continues to remain low
|
||||
battery-low-level = 15; #< notify if battery is lower than this percent
|
||||
# commands.battery = ""; #< optional action to perform when battery icon is clicked in the drop-down menu
|
||||
components = controlsSettingsComponents;
|
||||
click-closes = false;
|
||||
custom-items = [];
|
||||
css-name = "controls-window";
|
||||
hover-opens = false;
|
||||
icon-size = 16;
|
||||
interval = 1;
|
||||
leave-closes = false;
|
||||
menu.icon = "system-shutdown-symbolic";
|
||||
menu.items = [
|
||||
{
|
||||
name = "Lock";
|
||||
cmd = "s6-rc start ${locker}";
|
||||
}
|
||||
# {
|
||||
# name = "Logout";
|
||||
# cmd = "swaymsg exit";
|
||||
# }
|
||||
{
|
||||
name = "Reboot";
|
||||
cmd = "reboot";
|
||||
}
|
||||
{
|
||||
name = "Shutdown";
|
||||
cmd = "shutdown now";
|
||||
}
|
||||
];
|
||||
menu.name = "Exit";
|
||||
output-switcher = true; #< allow changing the default audio sink
|
||||
#v `show-<x>` means "show the NUMERICAL VALUE corresponding to <x>"
|
||||
# e.g. show-battery means "show the battery _percentage_ next to its icon".
|
||||
show-battery = true;
|
||||
show-brightness = false;
|
||||
show-values = false;
|
||||
show-volume = false;
|
||||
# window-width: should be 360 for moby, but because of weird `margin` tweaks in style.css
|
||||
# we have to add 20px to both sides
|
||||
window-width = 400;
|
||||
};
|
||||
playerctl = {
|
||||
button-css-name = "playerctl-button";
|
||||
buttons-position = "left";
|
||||
chars = playerctlChars;
|
||||
icon-size = 16;
|
||||
interval = 2;
|
||||
label-css-name = "playerctl-label";
|
||||
scroll = false;
|
||||
show-cover = false; #< don't show the little music-note icon
|
||||
show-previous = mediaPrevNext;
|
||||
show-next = mediaPrevNext;
|
||||
show-name = mediaPrevNext;
|
||||
};
|
||||
swaync = {
|
||||
css-name = "swaync-label";
|
||||
# interval = 1;
|
||||
# icon-placement = "left";
|
||||
# icon-size = 18;
|
||||
# tooltip-text = "";
|
||||
# on-left-click = "swaync-client -t";
|
||||
# on-right-click = "";
|
||||
# on-middle-click = "";
|
||||
# on-scroll-up = "";
|
||||
# on-scroll-down = "";
|
||||
# always-show-icon = true;
|
||||
};
|
||||
sway-workspaces = {
|
||||
angle = 0.0;
|
||||
custom-labels = [];
|
||||
focused-labels = [];
|
||||
hide-empty = workspaceHideEmpty;
|
||||
image-size = 16;
|
||||
mark-autotiling = true;
|
||||
mark-content = false;
|
||||
name-length = 40;
|
||||
numbers = workspaceNumbers;
|
||||
show-icon = windowIcon;
|
||||
show-layout = false;
|
||||
show-name = windowTitle;
|
||||
};
|
||||
|
||||
executor-sysload = {
|
||||
script = "sane-sysload {mem} {cpu}";
|
||||
interval = 10;
|
||||
css-name = "";
|
||||
on-right-click = "";
|
||||
icon-size = 16;
|
||||
show-icon = false;
|
||||
tooltip-text = "";
|
||||
on-left-click = "";
|
||||
on-middle-click = "";
|
||||
on-scroll-up = "";
|
||||
on-scroll-down = "";
|
||||
};
|
||||
|
||||
# unused modules:
|
||||
brightness-slider = {};
|
||||
dwl-tags = {};
|
||||
hyprland-taskbar = {};
|
||||
hyprland-workspaces = {};
|
||||
keyboard-layout = {};
|
||||
openweather = {};
|
||||
scratchpad = {};
|
||||
sway-mode = {};
|
||||
sway-taskbar = {}; #< windows-style taskbar, usually placed at the bottom of the screen, to show open windows & tab to them on click
|
||||
tray = {};
|
||||
}
|
||||
]
|
||||
|
|
@ -1,175 +0,0 @@
|
|||
# nwg-panel: a wayland status bar (like waybar, etc)
|
||||
# documentation is in the GitHub Wiki:
|
||||
# - <https://github.com/nwg-piotr/nwg-panel/wiki/Configuration>
|
||||
#
|
||||
# interactively configure with: `nwg-panel-config`
|
||||
# ^ note that this may interfere with the `nwg-panel` service
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.nwg-panel;
|
||||
mkEnableOption' = default: description: lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
inherit default description;
|
||||
};
|
||||
in
|
||||
{
|
||||
sane.programs.nwg-panel = {
|
||||
configOption = with lib; mkOption {
|
||||
default = {};
|
||||
type = types.submodule {
|
||||
options = {
|
||||
clockFontSize = mkOption {
|
||||
type = types.int;
|
||||
# what looks good:
|
||||
# - 15px on moby
|
||||
# - 24px on lappy
|
||||
# there's about 10px padding total around this (above + below)
|
||||
default = lib.min 24 (cfg.config.height - 11);
|
||||
};
|
||||
fontSize = mkOption {
|
||||
type = types.int;
|
||||
default = 16;
|
||||
};
|
||||
height = mkOption {
|
||||
type = types.int;
|
||||
default = 40;
|
||||
description = ''
|
||||
height of the top bar in px.
|
||||
'';
|
||||
};
|
||||
locker = mkOption {
|
||||
type = types.str;
|
||||
default = config.sane.programs.swayidle.config.actions.lock.service;
|
||||
description = ''
|
||||
s6 service to start which can lock the screen
|
||||
'';
|
||||
};
|
||||
battery = mkEnableOption' true "display battery status";
|
||||
brightness = mkEnableOption' true "display backlight level and slider";
|
||||
mediaTitle = mkEnableOption' true "display title of current song/media";
|
||||
mediaPrevNext = mkEnableOption' true "display prev/next button in media";
|
||||
sysload = mkEnableOption' true "display system load info (cpu/memory)";
|
||||
windowIcon = mkEnableOption' true "display icon of active window";
|
||||
windowTitle = mkEnableOption' true "display title of active window";
|
||||
workspaceNumbers = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [
|
||||
# TODO: workspace 10 should be rendered as "TV"
|
||||
"1" "2" "3" "4" "5" "6" "7" "8" "9" "10"
|
||||
];
|
||||
description = ''
|
||||
workspaces to monitor
|
||||
'';
|
||||
};
|
||||
workspaceHideEmpty = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
packageUnwrapped = (pkgs.nwg-panel.override {
|
||||
# XXX(2024/06/13): hyprland does not cross compile
|
||||
hyprland = null;
|
||||
# XXX(2024/06/13): wlr-randr does not cross compile
|
||||
wlr-randr = null; #< only used if not on sway/hyprland; or if using dwl
|
||||
}).overrideAttrs (base: {
|
||||
# patches = (base.patches or []) ++ lib.optionals (!cfg.config.mediaPrevNext) [
|
||||
# ./playerctl-no-prev-next.diff
|
||||
# ];
|
||||
patches = (base.patches or []) ++ [
|
||||
(pkgs.fetchpatch {
|
||||
# upstreaming: <https://github.com/nwg-piotr/nwg-panel/pull/309>
|
||||
url = "https://git.uninsane.org/colin/nwg-panel/commit/a714e4100c409feb02c454874d030d192bfb0ae5.patch";
|
||||
name = "playerctl: add settings to control which elements are displayed";
|
||||
hash = "sha256-OofS46wAI3EDE3JbYs/Nn+Vkw9TP1mwSFvk+vBERg2s=";
|
||||
})
|
||||
];
|
||||
|
||||
# - disable the drop-down chevron by the controls.
|
||||
# it's precious space on moby, doesn't do much to help on lappy either.
|
||||
# - disable brightness indicator for same reason.
|
||||
# - *leave* the volume indicator: one *could* remove it, however on desko that would leave the controls pane empty
|
||||
# making the dropdown inaccessible
|
||||
# also, remove padding from the items. i can manage that in css and the python padding prevents that.
|
||||
postPatch = (base.postPatch or "") + ''
|
||||
substituteInPlace nwg_panel/modules/controls.py --replace-fail \
|
||||
'self.box.pack_start(box, False, False, 6)' \
|
||||
'self.box.pack_start(box, False, False, 0)'
|
||||
|
||||
substituteInPlace nwg_panel/modules/controls.py --replace-fail \
|
||||
'box.pack_start(self.pan_image, False, False, 4)' \
|
||||
'# box.pack_start(self.pan_image, False, False, 0)'
|
||||
substituteInPlace nwg_panel/modules/controls.py --replace-fail \
|
||||
'box.pack_start(self.bri_image, False, False, 4)' \
|
||||
'# box.pack_start(self.bri_image, False, False, 0)'
|
||||
|
||||
substituteInPlace nwg_panel/modules/controls.py --replace-fail \
|
||||
'box.pack_start(self.vol_image, False, False, 4)' \
|
||||
'box.pack_start(self.vol_image, False, False, 0)'
|
||||
'';
|
||||
|
||||
# XXX(2024/06/13) the bluetooth stuff doesn't cross compile, so disable it
|
||||
propagatedBuildInputs = lib.filter (p: p.pname != "pybluez") base.propagatedBuildInputs;
|
||||
|
||||
strictDeps = true;
|
||||
});
|
||||
|
||||
suggestedPrograms = [
|
||||
"pactl" # pactl required by `per-app-volume` component.
|
||||
];
|
||||
|
||||
fs.".config/nwg-panel/style.css".symlink.target = pkgs.substituteAll {
|
||||
src = ./style.css;
|
||||
inherit (cfg.config) fontSize clockFontSize;
|
||||
};
|
||||
fs.".config/nwg-panel/common-settings.json".symlink.target = ./common-settings.json;
|
||||
fs.".config/nwg-panel/config".symlink.target = pkgs.writers.writeJSON "config" (import ./config.nix {
|
||||
inherit (cfg.config) locker height mediaPrevNext windowIcon windowTitle workspaceHideEmpty workspaceNumbers;
|
||||
# component order matters, mostly for the drop-down.
|
||||
# default for most tools (e.g. swaync) is brightness control above volume.
|
||||
controlsSettingsComponents =
|
||||
lib.optionals cfg.config.brightness [
|
||||
"brightness"
|
||||
] ++ [
|
||||
"volume"
|
||||
"per-app-volume"
|
||||
] ++ lib.optionals cfg.config.battery [
|
||||
"battery"
|
||||
]
|
||||
;
|
||||
modulesRight = [
|
||||
"playerctl"
|
||||
] ++ lib.optionals cfg.config.sysload [
|
||||
"executor-sysload"
|
||||
];
|
||||
playerctlChars = if cfg.config.mediaTitle then 60 else 0;
|
||||
});
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistAudio = true;
|
||||
sandbox.whitelistDri = true;
|
||||
sandbox.whitelistS6 = true;
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.whitelistDbus = [
|
||||
"user" # playerctl, swaync, ...
|
||||
];
|
||||
sandbox.extraPaths = [
|
||||
# for the battery indicator
|
||||
"/sys/class/power_supply"
|
||||
"/sys/devices"
|
||||
];
|
||||
sandbox.extraRuntimePaths = [ "sway" ];
|
||||
|
||||
services.nwg-panel = {
|
||||
description = "nwg-panel status/topbar for wayland";
|
||||
partOf = [ "graphical-session" ];
|
||||
|
||||
# to debug styling, run with GTK_DEBUG=interactive
|
||||
# N.B.: G_MESSAGES_DEBUG=all causes the swaync icon to not render
|
||||
# command = "env G_MESSAGES_DEBUG=all nwg-panel";
|
||||
command = "nwg-panel";
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
commit 7aa759990b38b09abf9010dfe58e4cbdc1493282 (HEAD -> dev-sane)
|
||||
Author: Colin <colin@uninsane.org>
|
||||
Date: 2024-06-15 21:41:46 +0000
|
||||
|
||||
playerctl: remove backward/forward/music-note icons
|
||||
|
||||
these aren't worth the space cost on narrow devices (moby)
|
||||
|
||||
diff --git a/nwg_panel/modules/playerctl.py b/nwg_panel/modules/playerctl.py
|
||||
index ff48d4c..43ae221 100644
|
||||
--- a/nwg_panel/modules/playerctl.py
|
||||
+++ b/nwg_panel/modules/playerctl.py
|
||||
@@ -211,15 +211,6 @@ class Playerctl(Gtk.EventBox):
|
||||
if self.settings["angle"] != 0.0:
|
||||
button_box.set_orientation(Gtk.Orientation.VERTICAL)
|
||||
|
||||
- img = Gtk.Image()
|
||||
- update_image(img, "media-skip-backward-symbolic", self.settings["icon-size"], icons_path=self.icons_path)
|
||||
- btn = Gtk.Button()
|
||||
- btn.set_image(img)
|
||||
- if self.settings["button-css-name"]:
|
||||
- btn.set_property("name", self.settings["button-css-name"])
|
||||
- btn.connect("clicked", self.launch, self.PlayerOps.PREVIOUS)
|
||||
- button_box.pack_start(btn, False, False, 1)
|
||||
-
|
||||
self.play_pause_btn = Gtk.Button()
|
||||
if self.settings["button-css-name"]:
|
||||
self.play_pause_btn.set_property("name", self.settings["button-css-name"])
|
||||
@@ -229,15 +220,6 @@ class Playerctl(Gtk.EventBox):
|
||||
self.play_pause_btn.connect("clicked", self.launch, self.PlayerOps.PLAY_PAUSE)
|
||||
button_box.pack_start(self.play_pause_btn, False, False, 1)
|
||||
|
||||
- img = Gtk.Image()
|
||||
- update_image(img, "media-skip-forward-symbolic", self.settings["icon-size"], icons_path=self.icons_path)
|
||||
- btn = Gtk.Button()
|
||||
- btn.set_image(img)
|
||||
- if self.settings["button-css-name"]:
|
||||
- btn.set_property("name", self.settings["button-css-name"])
|
||||
- btn.connect("clicked", self.launch, self.PlayerOps.NEXT)
|
||||
- button_box.pack_start(btn, False, False, 1)
|
||||
-
|
||||
self.num_players_lbl = Gtk.Label.new("")
|
||||
if self.settings["label-css-name"]:
|
||||
self.num_players_lbl.set_property("name", self.settings["label-css-name"])
|
||||
@@ -257,13 +239,9 @@ class Playerctl(Gtk.EventBox):
|
||||
self.box.pack_start(button_box, False, False, 2)
|
||||
if self.settings["show-cover"]:
|
||||
self.box.pack_start(self.cover_img, False, False, 0)
|
||||
- self.box.pack_start(self.num_players_lbl, False, False, 0)
|
||||
- self.box.pack_start(self.label, False, False, 5)
|
||||
else:
|
||||
if self.settings["show-cover"]:
|
||||
self.box.pack_start(self.cover_img, False, False, 2)
|
||||
- self.box.pack_start(self.num_players_lbl, False, False, 0)
|
||||
- self.box.pack_start(self.label, False, False, 2)
|
||||
self.box.pack_start(button_box, False, False, 10)
|
||||
|
||||
def launch(self, button, op):
|
||||
|
|
@ -1,241 +0,0 @@
|
|||
/* foreground (text)/background */
|
||||
@define-color fg0 #d8d8d8;
|
||||
@define-color fg1 #ffffff;
|
||||
@define-color bg0 #130c0c;
|
||||
@define-color bg1 #1c1716;
|
||||
/* green accents */
|
||||
@define-color accent-g0 #1f5e54;
|
||||
@define-color accent-g1 #418379;
|
||||
@define-color accent-g2 #63a89c;
|
||||
/* red accents */
|
||||
@define-color accent-r0 #c96262;
|
||||
@define-color accent-r1 #d27871;
|
||||
@define-color accent-r2 #ff968b;
|
||||
/* light (teal-white) accents */
|
||||
@define-color accent-l0 #e1f0ef;
|
||||
@define-color accent-l1 #f9fffc;
|
||||
|
||||
|
||||
* {
|
||||
font-size: @fontSize@px;
|
||||
}
|
||||
|
||||
#task-box-focused {
|
||||
background-color: @accent-g2;
|
||||
}
|
||||
|
||||
#playerctl-button {
|
||||
background-color: rgba(0, 0, 0, 0.08);
|
||||
background-image: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
/* remove the 1px gap between buttons, since that causes color stripes if the background is a different color */
|
||||
margin-left: -1;
|
||||
margin-right: -1;
|
||||
outline: none;
|
||||
/* prevent the buttons from pushing the whole bar down */
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
#panel-top {
|
||||
background: @accent-g1;
|
||||
color: @fg1;
|
||||
}
|
||||
/* fix up the top bar sections so that the clock can be centered, even without forcing it to take 1/3rd of the bar */
|
||||
/* pair with `homogenous = false` in config. on overflow, the clock may be rendered on top of the left portion of the bar */
|
||||
/* and the right portion of the bar will render on top of all */
|
||||
#panel-top > box > box > box > #left-box {
|
||||
margin-left: 0px;
|
||||
margin-right: -16384px;
|
||||
}
|
||||
#panel-top > box > box > box > #center-box {
|
||||
margin-left: 0px;
|
||||
margin-right: 0px;
|
||||
}
|
||||
#panel-top > box > box > box > box {
|
||||
/* this is the *parent* of #right-box, which is uniquely under an unnamed "helper box" */
|
||||
/* i have to address this parent, because otherwise only the controls are visible and the executors (including playerctl) */
|
||||
/* are packed in a fill mode that pushes them off the visible section of the bar */
|
||||
margin-left: -16384px;
|
||||
}
|
||||
|
||||
#right-box > widget > * {
|
||||
/* TODO: tune this for moby */
|
||||
padding-right: 3px;
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
#swaync-label {
|
||||
/* move the notification count closer to the bell icon */
|
||||
margin-left: -3px;
|
||||
/* TODO: this should be main font size -1 */
|
||||
font-size: 14px;
|
||||
color: @accent-r2;
|
||||
}
|
||||
|
||||
/* increase the size of each workspace icon */
|
||||
#sway-workspaces-item > label {
|
||||
padding-left: 1px;
|
||||
padding-right: 1px;
|
||||
}
|
||||
/* default config highlights hovered workspace with a gray border */
|
||||
#sway-workspaces > widget:selected {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* the CSS nodes are difficult to determine.
|
||||
* reference: <https://github.com/numixproject/numix-gtk-theme/blob/master/src/gtk-3.20/scss/widgets/_calendar.scss>
|
||||
*/
|
||||
#calendar-window {
|
||||
background-color: @accent-l0;
|
||||
}
|
||||
calendar {
|
||||
background-color: @accent-l0;
|
||||
}
|
||||
calendar :selected {
|
||||
background-color: @accent-r1;
|
||||
}
|
||||
|
||||
|
||||
#controls-window {
|
||||
border-radius: 15px;
|
||||
background: @bg1;
|
||||
color: @fg1;
|
||||
}
|
||||
|
||||
/* default config highlights selected items with a green border */
|
||||
widget:selected {
|
||||
box-shadow: none;
|
||||
background-color: @accent-g0;
|
||||
}
|
||||
#controls-window widget:selected {
|
||||
box-shadow: none;
|
||||
background-color: @accent-r0;
|
||||
}
|
||||
|
||||
/* default config puts a *ridiculous* amount of padding around the whole controls window */
|
||||
#controls-window > widget > .vertical {
|
||||
margin-left: -20px;
|
||||
margin-right: -20px;
|
||||
}
|
||||
#controls-window > widget > .vertical > .horizontal {
|
||||
margin-top: -14px; /* full reset would be -20px */
|
||||
margin-bottom: -20px;
|
||||
}
|
||||
/* add back in a little bit of padding, but in a way such that my highlights apply to it */
|
||||
#controls-window .horizontal widget > box,
|
||||
#controls-window > widget > .vertical > .horizontal > .vertical > .horizontal
|
||||
{
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
#controls-window > widget > .vertical > .horizontal > .vertical > widget > box
|
||||
{
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
#controls-window > widget > .vertical > .horizontal > .vertical > box > widget > box
|
||||
{
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
/* hierarchy is .horizontal > {image, scale > { value, contents > trough > { slider, highlight } } } */
|
||||
scale {
|
||||
padding-right: 0px;
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
scale trough {
|
||||
padding-left: 9px;
|
||||
padding-right: 9px;
|
||||
border-radius: 9px;
|
||||
border-color: rgba(0, 0, 0, 0);
|
||||
background: @bg0;
|
||||
}
|
||||
scale highlight {
|
||||
border-radius: 9px;
|
||||
border-color: rgba(0, 0, 0, 0);
|
||||
margin: 0px;
|
||||
margin-left: -9px;
|
||||
background: @accent-r1;
|
||||
}
|
||||
scale slider {
|
||||
margin-top: -3px;
|
||||
margin-bottom: -3px;
|
||||
background: @accent-l1;
|
||||
min-height: 25px;
|
||||
min-width: 25px;
|
||||
}
|
||||
|
||||
#clock {
|
||||
font-family: monospace;
|
||||
font-size: @clockFontSize@px;
|
||||
}
|
||||
|
||||
/* UNUSED IN MY CURRENT CONFIG: COPIED FROM SAMPLE CONFIG */
|
||||
|
||||
/* Controls window in sample config uses this name */
|
||||
/* Brightness slider popup window in sample config uses this name */
|
||||
#brightness-popup {
|
||||
border-radius: 15px;
|
||||
background: @bg1;
|
||||
color: @fg1;
|
||||
}
|
||||
#brightness-popup box {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
/* Executors usually behave better in monospace fonts */
|
||||
#executor-label {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
/* Bottom panel in sample config uses this name */
|
||||
#panel-bottom {
|
||||
background: #101010;
|
||||
color: #eeeeee;
|
||||
}
|
||||
|
||||
/* Sample executor-weather uses "css-name": "weather" */
|
||||
#weather {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* dwl-tags module */
|
||||
#dwl-tag-box {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
#dwl-tag-occupied {
|
||||
font-family: monospace;
|
||||
color: #eee;
|
||||
background-color: #006699;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
#dwl-tag-free {
|
||||
font-family: monospace;
|
||||
color: #eee;
|
||||
background-color: rgba (32, 50, 90, 1.0);
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
#dwl-tag-urgent {
|
||||
font-family: monospace;
|
||||
color: #eee;
|
||||
background-color: #ee6600;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
#dwl-tag-selected {
|
||||
border: solid 2px;
|
||||
border-color: #81a1c1;
|
||||
}
|
|
@ -4,7 +4,5 @@
|
|||
# binutils-unwrapped is like 80 MiB, just for this one binary;
|
||||
# dynamic linking means copying the binary doesn't reduce the closure much at all compared to just symlinking it.
|
||||
packageUnwrapped = pkgs.linkIntoOwnPackage pkgs.binutils-unwrapped "bin/objdump";
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.autodetectCliPaths = "existingFile";
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
# OLS: Offline Location Service: <https://codeberg.org/tpikonen/ols>
|
||||
# fields {wifi SSID,cell tower} -> lat/long queries from geoclue
|
||||
# satisfies queries via https://wigle.net, by learning about map tiles
|
||||
# and caching those on-disk so that repeat queries may be serviced offline.
|
||||
#
|
||||
# it listens on localhost:8088, and one can validate its operation with a query like (substitute macAddresses for something real):
|
||||
# - WiFi: curl -d '{"wifiAccessPoints":[{"macAddress":"01:23:45:67:89:ab","signalStrength":-78},{"macAddress":"cd:ef:01:23:45:56","signalStrength":-76}]}' http://127.0.0.1:8088/v1/geolocate
|
||||
# - Cell: curl -d '{"cellTowers":[{ "radioType": "lte", "mobileCountryCode": 310, "mobileNetworkCode": 260, "locationAreaCode": NNNNN, "cellId": MMMMMMMM }]}' http://127.0.0.1:8088/v1/geolocate
|
||||
#
|
||||
## wigle docs:
|
||||
# - IRC: #wigle on WiGLE.net:6667
|
||||
# - API: <https://api.wigle.net/swagger>
|
||||
# API return codes:
|
||||
# - 429: "too many queries today."
|
||||
#
|
||||
# rate limiting:
|
||||
# - as a new user you'll be limited to something ridiculous like 5 queries per day.
|
||||
# supposedly this improves "based on history and participation".
|
||||
# - source: <https://api.wigle.net/swagger#/Network%20search%20and%20information%20tools/search_2>
|
||||
# - "API for some functions is limited on a daily basis for all users for the time being, but if you'd like increased access, please email us (include your username and usecase) at WiGLE-admin@wigle.net."
|
||||
# - source: <https://wigle.net/account>
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs.ols = {
|
||||
packageUnwrapped = pkgs.geoclue-ols;
|
||||
|
||||
fs.".config/ols/cell.db".symlink.target = pkgs.runCommandLocal "cell.db" {
|
||||
nativeBuildInputs = [ pkgs.geoclue-ols ];
|
||||
} ''
|
||||
cellid-ols-import -o "$out" "${pkgs.opencellid}"
|
||||
'';
|
||||
|
||||
persist.byStore.private = [
|
||||
".local/share/ols"
|
||||
];
|
||||
|
||||
secrets.".config/ols/ols.toml" = ../../../secrets/common/ols.toml.bin;
|
||||
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.net = "all";
|
||||
|
||||
services.ols = {
|
||||
description = "ols: Offline Location Service";
|
||||
command = "ols 2>&1"; # XXX: it logs to stderr, and my s6 infrastructure apparently doesn't handle that
|
||||
partOf = [ "graphical-session" ];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs.pactl = {
|
||||
packageUnwrapped = pkgs.linkIntoOwnPackage pkgs.pulseaudio "bin/pactl";
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistAudio = true;
|
||||
};
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs.pidof = {
|
||||
packageUnwrapped = pkgs.linkIntoOwnPackage pkgs.procps "bin/pidof";
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.isolatePids = false;
|
||||
};
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs.pkill = {
|
||||
packageUnwrapped = pkgs.linkIntoOwnPackage pkgs.procps "bin/pkill";
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.isolatePids = false;
|
||||
};
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.qmk-udev-rules;
|
||||
in
|
||||
{
|
||||
sane.programs.qmk-udev-rules.sandbox.enable = false;
|
||||
services.udev.packages = lib.mkIf cfg.enabled [ cfg.package ];
|
||||
}
|
||||
|
|
@ -55,12 +55,6 @@ let
|
|||
mimeTypes = [ "application/x-desktop" ];
|
||||
noDisplay = true;
|
||||
})
|
||||
(pkgs.makeDesktopItem {
|
||||
name = "close";
|
||||
exec = "true";
|
||||
desktopName = "Close Menu";
|
||||
mimeTypes = [ "application/x-desktop" ];
|
||||
})
|
||||
];
|
||||
});
|
||||
# rofi-emoji = pkgs.rofi-emoji.override {
|
||||
|
@ -122,8 +116,6 @@ in
|
|||
|
||||
fs.".config/rofi/config.rasi".symlink.target = ./config.rasi;
|
||||
fs."Apps".symlink.target = ".local/share/applications/rofi-applications.desktop";
|
||||
fs."WiFi".symlink.target = ".local/share/applications/networkmanager_dmenu.desktop";
|
||||
fs."close".symlink.target = ".local/share/applications/close.desktop"; #< provide an escape from the file browser
|
||||
persist.byStore.cryptClearOnBoot = [
|
||||
# this gets us a few things:
|
||||
# - file browser remembers its last directory
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p bash -p sane-open
|
||||
#!nix-shell -i bash -p sane-open
|
||||
|
||||
# use:
|
||||
# rofi-run-command <handler>.desktop [cmd [args ...]]
|
||||
|
@ -16,8 +16,7 @@ binArgs=("$@")
|
|||
if [ "$desktop" != .desktop ]; then
|
||||
exec sane-open --auto-keyboard --application "$desktop"
|
||||
elif [ "$binary" = "xdg-open" ]; then
|
||||
# the file we want to open could be a symlink to a .desktop file, so omit `--file` and let sane-open figure that part out for itself:
|
||||
exec sane-open --auto-keyboard "${binArgs[@]}"
|
||||
exec sane-open --auto-keyboard --file "${binArgs[@]}"
|
||||
fi
|
||||
|
||||
printf "no .desktop file, and unexpected binary; not invoking: %s %s\n" "$binary" "${binArgs[*]}" >&2
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p bash -p gnused -p rofi -p wtype
|
||||
#!nix-shell -i bash -p gnused -p rofi -p wtype
|
||||
|
||||
# "bookmarking"/snippets inspired by Luke Smith:
|
||||
# - <https://www.youtube.com/watch?v=d_11QaTlf1I>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
sane.programs.s6-rc = {
|
||||
packageUnwrapped = pkgs.s6-rc.overrideAttrs (upstream: {
|
||||
nativeBuildInputs = (upstream.nativeBuildInputs or []) ++ (with pkgs; [
|
||||
makeWrapper # requires shell wrapper -- not binary -- so that env var args can be expanded by the wrapper
|
||||
makeWrapper
|
||||
]);
|
||||
# s6-rc looks for files in /run/s6/{live,compiled,...} by default.
|
||||
# let's patch that to be a user-specific runtime dir, since i run it as an ordinary user.
|
||||
|
@ -11,7 +11,7 @@
|
|||
postInstall = (upstream.postInstall or "") + ''
|
||||
for prog in s6-rc s6-rc-bundle s6-rc-db s6-rc-format-upgrade s6-rc-init s6-rc-update; do
|
||||
wrapProgram "$bin/bin/$prog" \
|
||||
--add-flags '-l' --add-flags '$XDG_RUNTIME_DIR/s6/live'
|
||||
--add-flags '-l $XDG_RUNTIME_DIR/s6/live'
|
||||
done
|
||||
'';
|
||||
});
|
||||
|
|
|
@ -83,7 +83,7 @@ in
|
|||
"jq"
|
||||
"killall"
|
||||
"playerctl"
|
||||
"procps" #< TODO: reduce to just those parts of procps which are really needed
|
||||
"procps"
|
||||
"sane-open"
|
||||
# "sway" #< TODO: circular dependency :-(
|
||||
"wireplumber"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p bash -p coreutils -p jq -p killall -p playerctl -p procps -p sane-open -p sway -p util-linux -p wireplumber
|
||||
#!nix-shell -i bash -p coreutils -p jq -p killall -p playerctl -p procps -p sane-open -p sway -p util-linux -p wireplumber
|
||||
# vim: set filetype=bash :
|
||||
|
||||
# input map considerations
|
||||
|
|
|
@ -206,13 +206,7 @@ in
|
|||
];
|
||||
};
|
||||
|
||||
"sane-scripts.stop-all-servo".sandbox = {
|
||||
method = "bwrap";
|
||||
extraPaths = [
|
||||
"/run/dbus"
|
||||
"/run/systemd"
|
||||
];
|
||||
};
|
||||
"sane-scripts.stop-all-servo" = {};
|
||||
|
||||
# if `tee` isn't trustworthy we have bigger problems
|
||||
"sane-scripts.sudo-redirect".sandbox.enable = false;
|
||||
|
@ -285,6 +279,5 @@ in
|
|||
".persist/private/.mozilla"
|
||||
];
|
||||
};
|
||||
"sane-scripts.wipe".suggestedPrograms = [ "pkill" ];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
{ ... }:
|
||||
{
|
||||
sane.programs.sane-sysload = {
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.extraPaths = [
|
||||
"/sys/class/power_supply"
|
||||
"/sys/devices"
|
||||
];
|
||||
};
|
||||
}
|
|
@ -106,7 +106,7 @@ let
|
|||
yaru-theme
|
||||
zuki-themes
|
||||
;
|
||||
inherit (pkgs)
|
||||
inherit (pkgs.gnome)
|
||||
adwaita-icon-theme
|
||||
gnome-themes-extra
|
||||
;
|
||||
|
@ -120,11 +120,11 @@ let
|
|||
Dracula-cursors = dracula-theme;
|
||||
};
|
||||
cursor-theme = {
|
||||
Adwaita = adwaita-icon-theme;
|
||||
Adwaita = gnome.adwaita-icon-theme;
|
||||
};
|
||||
gtk-theme = {
|
||||
Adwaita = gnome-themes-extra; # gtk-3.0
|
||||
Adwaita-dark = gnome-themes-extra; # gtk-3.0
|
||||
Adwaita = gnome.gnome-themes-extra; # gtk-3.0
|
||||
Adwaita-dark = gnome.gnome-themes-extra; # gtk-3.0
|
||||
Arc = arc-theme; # gtk-4.0
|
||||
Arc-Dark = arc-theme; # gtk-4.0
|
||||
Arc-Darker = arc-theme; # gtk-4.0
|
||||
|
@ -137,7 +137,7 @@ let
|
|||
Fluent-Dark-compact = fluent-gtk-theme; # gtk-4.0
|
||||
Fluent-Light = fluent-gtk-theme; # gtk-4.0
|
||||
Fluent-Light-compact = fluent-gtk-theme; # gtk-4.0, NICE!
|
||||
HighContrast = gnome-themes-extra; # gtk-3.0
|
||||
HighContrast = gnome.gnome-themes-extra; # gtk-3.0
|
||||
Matcha-light-azul = matcha-gtk-theme; # gtk-4.0, NICE!
|
||||
Matcha-light-sea = matcha-gtk-theme; # gtk-4.0, NICE!
|
||||
# additional Matcha-* omitted
|
||||
|
@ -182,13 +182,13 @@ let
|
|||
# - that's probably why so many themes here also symlink Adwaita
|
||||
# my accounting of "adwaita coverage" seems to be overoptimistic somehow
|
||||
# maybe some apps bundle adwaita themselves
|
||||
Adwaita = adwaita-icon-theme;
|
||||
Adwaita = gnome.adwaita-icon-theme;
|
||||
Arc = arc-icon-theme; # 4.5/5, meh icon for "vertical ellipsis". 3/5 adwaita coverage
|
||||
elementary-xfce = elementary-xfce-icon-theme; # does not cross compile (2023/10/03)
|
||||
elementary-xfce-dark = elementary-xfce-icon-theme;
|
||||
elementary-xfce-darker = elementary-xfce-icon-theme;
|
||||
elementary-xfce-darkest = elementary-xfce-icon-theme;
|
||||
HighContrast = gnome-themes-extra; # 5/5. 5/5 adwaita coverage (4/5 cross)
|
||||
HighContrast = gnome.gnome-themes-extra; # 5/5. 5/5 adwaita coverage (4/5 cross)
|
||||
Humanity = humanity-icon-theme; # 5/5. 5/5 adwaita coverage (3.5/5 cross, unique in which icons work)
|
||||
Humanity-Dark = humanity-icon-theme;
|
||||
kora = kora-icon-theme;
|
||||
|
|
|
@ -44,19 +44,19 @@ in
|
|||
# XXX: /run/current-system symlink can't be cached without forcing regular mass rebuilds:
|
||||
# mount it as if it were a directory instead.
|
||||
"/run/current-system" = "";
|
||||
} // lib.optionalAttrs config.hardware.graphics.enable {
|
||||
} // lib.optionalAttrs config.hardware.opengl.enable {
|
||||
"/run/opengl-driver" = let
|
||||
gl = config.hardware.graphics;
|
||||
# from: <repo:nixos/nixpkgs:nixos/modules/hardware/graphics.nix>
|
||||
gl = config.hardware.opengl;
|
||||
# from: <repo:nixos/nixpkgs:nixos/modules/hardware/opengl.nix>
|
||||
package = pkgs.buildEnv {
|
||||
name = "opengl-drivers";
|
||||
paths = [ gl.package ] ++ gl.extraPackages;
|
||||
};
|
||||
in "${package}";
|
||||
} // lib.optionalAttrs (config.hardware.graphics.enable && config.hardware.graphics.enable32Bit) {
|
||||
} // lib.optionalAttrs (config.hardware.opengl.enable && config.hardware.opengl.driSupport32Bit) {
|
||||
"/run/opengl-driver-32" = let
|
||||
gl = config.hardware.graphics;
|
||||
# from: <repo:nixos/nixpkgs:nixos/modules/hardware/graphics.nix>
|
||||
gl = config.hardware.opengl;
|
||||
# from: <repo:nixos/nixpkgs:nixos/modules/hardware/opengl.nix>
|
||||
package = pkgs.buildEnv {
|
||||
name = "opengl-drivers-32bit";
|
||||
paths = [ gl.package32 ] ++ gl.extraPackages32;
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
# satellite-gtk: <https://codeberg.org/tpikonen/satellite>
|
||||
# - presents GPS tracking *details* in a graphical way
|
||||
# - shows which satellites are in view, their SNR, and the subset currently being used for triangulation
|
||||
# - shows fix coordinates (time, lat, long, altitude, speed)
|
||||
#
|
||||
### how to read the bargraph (example):
|
||||
#
|
||||
# 14 | XXXXXXXXXXXXX 26
|
||||
# 76 | =========== 23
|
||||
# 15 | XXXXXXXX 16
|
||||
# =
|
||||
# +----------------|
|
||||
# 0 30
|
||||
#
|
||||
# ^ this view means:
|
||||
# - GPS is receiving from sats 14, 76, 15 (by PRN) -- this comes from GSGSV NMEA data (Satellites in-View)
|
||||
# - sat 14 and 15 (shaded solid) are "active" -- this comes from GSGSA NMEA data ("Satellites Active")
|
||||
# - i believe "active" means "this sat was used in the most recent solution"
|
||||
#
|
||||
### text fields
|
||||
# - Modes (GP,GL,GA) ...
|
||||
# one letter each, indicating the mode for GPS, GLONASS, Galileo sats:
|
||||
# - N = no fix
|
||||
# - A = autonomous
|
||||
# - D = differential mode
|
||||
# - E = estimated (dead reckoning)
|
||||
# - etc
|
||||
# - Active / in use sats
|
||||
# - A/U, where A = number of satellites used in the previous fix,
|
||||
# U = number of satellites mentioned in latest GNS + GGA messages
|
||||
# - Receiving sats
|
||||
# shows the count of sats with non-zero SNR, from GSV messages
|
||||
# - Visible sats
|
||||
# shows the count of sats from GSV messages
|
||||
# - Age of update / fix
|
||||
# - Sys. Time
|
||||
# - Latitude
|
||||
# - Longitude
|
||||
# - Altitude
|
||||
# - Geoidal separation
|
||||
# - Speed
|
||||
# - True Course
|
||||
# - PDOP/HDOP/VDOP
|
||||
# - shows how sensitive the reported location is to measurement error (low values are better)
|
||||
# - HDOP = horizontal sensitivity, VDOP = vertical sensitivity, PDOP = positional (d) sensitivity
|
||||
# - 1-2 => excellent fix
|
||||
# - >10 => low confidence fix; recommended to discard the fix
|
||||
# - <https://en.wikipedia.org/wiki/Dilution_of_precision_(navigation)>
|
||||
#
|
||||
{ ... }:
|
||||
{
|
||||
sane.programs.satellite = {};
|
||||
}
|
|
@ -44,8 +44,6 @@ in
|
|||
".config/Signal"
|
||||
];
|
||||
|
||||
buildCost = 1;
|
||||
|
||||
services.signal-desktop = {
|
||||
description = "signal-desktop Signal Messenger client";
|
||||
# depends = [ "graphical-session" ];
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p bash -p jq -p sway -p util-linux
|
||||
#!nix-shell -i bash -p jq -p sway -p util-linux
|
||||
|
||||
help() {
|
||||
echo "queries the focused window and apply an appropriate display-wide scale."
|
||||
|
|
|
@ -148,8 +148,6 @@ in
|
|||
"fontconfig"
|
||||
# "gnome.gnome-bluetooth" # XXX(2023/05/14): broken
|
||||
# "gnome.gnome-control-center" # XXX(2023/06/28): depends on webkitgtk4_1
|
||||
"networkmanager_dmenu"
|
||||
"nwg-panel"
|
||||
"pipewire"
|
||||
"playerctl" # for waybar & particularly to have playerctld running
|
||||
"rofi" # menu/launcher
|
||||
|
@ -164,9 +162,9 @@ in
|
|||
"swayidle" # enable if you need it
|
||||
"swaynotificationcenter" # notification daemon
|
||||
"switchboard" # network/bluetooth/sound control panel
|
||||
"syshud" # volume notifier
|
||||
"sysvol" # volume notifier
|
||||
"unl0kr" # greeter
|
||||
# "waybar"
|
||||
"waybar" # used by sway config
|
||||
"wdisplays" # like xrandr
|
||||
"wireplumber" # used by sway config
|
||||
"wl-clipboard"
|
||||
|
@ -199,7 +197,6 @@ in
|
|||
sandbox.whitelistAudio = true; # it runs playerctl directly
|
||||
sandbox.whitelistDbus = [ "system" "user" ]; # to e.g. launch apps
|
||||
sandbox.whitelistDri = true;
|
||||
sandbox.whitelistS6 = true; #< for Super+L to start the screen locker service
|
||||
sandbox.whitelistX = true; # sway invokes xwayland itself
|
||||
sandbox.whitelistWayland = true;
|
||||
sandbox.extraRuntimePaths = [
|
||||
|
@ -229,7 +226,7 @@ in
|
|||
'';
|
||||
|
||||
fs.".config/sway/config".symlink.target = pkgs.substituteAll {
|
||||
src = ./config;
|
||||
src = ./sway-config;
|
||||
inherit (cfg.config)
|
||||
extra_lines
|
||||
font
|
||||
|
|
|
@ -32,7 +32,6 @@ in
|
|||
};
|
||||
sandbox.method = "bwrap";
|
||||
sandbox.whitelistS6 = true;
|
||||
sandbox.isolatePids = false; #< XXX: not sure why, but swaync segfaults under load without this!
|
||||
};
|
||||
|
||||
sane.programs.swaync-fbcli = {
|
||||
|
@ -62,8 +61,6 @@ in
|
|||
name of entry in /sys/class/backlight which indicates the primary backlight.
|
||||
'';
|
||||
};
|
||||
enableBacklight = mkEnableOption "include a backlight slider in the swaync dropdown (requires an active session with systemd-logind)";
|
||||
enableMpris = (mkEnableOption "show the currently playing media in the swaync dropdown, and navigation buttons") // { default = true; };
|
||||
};
|
||||
};
|
||||
default = {};
|
||||
|
@ -119,14 +116,27 @@ in
|
|||
env.GNOTIFICATION_BACKEND = "freedesktop";
|
||||
|
||||
fs.".config/swaync/style.css".symlink.target = ./style.css;
|
||||
fs.".config/swaync/config.json".symlink.target = pkgs.writers.writeJSON "config.json" {
|
||||
fs.".config/swaync/config.json".symlink.text = builtins.toJSON {
|
||||
"$schema" = "/etc/xdg/swaync/configSchema.json";
|
||||
control-center-height = 600;
|
||||
positionX = "right";
|
||||
positionY = "top";
|
||||
layer = "overlay";
|
||||
control-center-layer = "top";
|
||||
control-center-margin-bottom = 0;
|
||||
control-center-margin-left = 0;
|
||||
control-center-margin-right = 0;
|
||||
layer-shell = true;
|
||||
cssPriority = "user"; # "application"|"user". "user" in order to override the system gtk theme.
|
||||
control-center-margin-top = 0;
|
||||
control-center-margin-bottom = 0;
|
||||
control-center-margin-right = 0;
|
||||
control-center-margin-left = 0;
|
||||
notification-2fa-action = true;
|
||||
notification-inline-replies = false;
|
||||
notification-icon-size = 64;
|
||||
notification-body-image-height = 100;
|
||||
notification-body-image-width = 200;
|
||||
timeout = 30;
|
||||
timeout-low = 5;
|
||||
timeout-critical = 0;
|
||||
fit-to-screen = true; #< have notification center take full vertical screen space
|
||||
# control-center-width:
|
||||
# pinephone native display is 720 x 1440
|
||||
# - for compositor scale=2.0 => 360
|
||||
|
@ -134,28 +144,14 @@ in
|
|||
# - for compositor scale=1.6 => 450
|
||||
# if it's set to something wider than the screen, then it overflows and items aren't visible.
|
||||
control-center-width = 360;
|
||||
cssPriority = "user"; # "application"|"user". "user" in order to override the system gtk theme.
|
||||
fit-to-screen = true; #< have notification center take full vertical screen space
|
||||
hide-on-action = true;
|
||||
hide-on-clear = true; #< hide control center when clicking "clear all"
|
||||
image-visibility = "when-available";
|
||||
keyboard-shortcuts = true;
|
||||
layer = "overlay";
|
||||
layer-shell = true;
|
||||
notification-2fa-action = true;
|
||||
notification-body-image-height = 100;
|
||||
notification-body-image-width = 200;
|
||||
notification-icon-size = 64;
|
||||
notification-inline-replies = false;
|
||||
control-center-height = 600;
|
||||
notification-window-width = 360;
|
||||
positionX = "right";
|
||||
positionY = "top";
|
||||
script-fail-notify = true;
|
||||
timeout = 30;
|
||||
timeout-critical = 0;
|
||||
timeout-low = 5;
|
||||
keyboard-shortcuts = true;
|
||||
image-visibility = "when-available";
|
||||
transition-time = 100;
|
||||
|
||||
hide-on-clear = true; #< hide control center when clicking "clear all"
|
||||
hide-on-action = true;
|
||||
script-fail-notify = true;
|
||||
inherit scripts;
|
||||
widgets = [
|
||||
# what to show in the notification center (and in which order).
|
||||
|
@ -168,13 +164,9 @@ in
|
|||
"dnd"
|
||||
"inhibitors"
|
||||
"buttons-grid"
|
||||
] ++ lib.optionals cfg.config.enableBacklight [
|
||||
"backlight"
|
||||
] ++ [
|
||||
"volume"
|
||||
] ++ lib.optionals cfg.config.enableMpris [
|
||||
"mpris"
|
||||
] ++ [
|
||||
"notifications"
|
||||
];
|
||||
widget-config = {
|
||||
|
@ -200,7 +192,7 @@ in
|
|||
buttons.dissent
|
||||
] ++ lib.optionals config.sane.programs.signal-desktop.enabled [
|
||||
buttons.signal-desktop
|
||||
] ++ lib.optionals config.sane.programs.geary.enabled [
|
||||
] ++ lib.optionals config.sane.programs."gnome.geary".enabled [
|
||||
buttons.geary
|
||||
];
|
||||
};
|
||||
|
@ -236,10 +228,7 @@ in
|
|||
depends = [ "sound" ]; #< TODO: else it will NEVER see the pulse socket in its sandbox
|
||||
partOf = [ "graphical-session" ];
|
||||
|
||||
# N.B.: G_MESSAGES_DEBUG=all breaks DND mode:
|
||||
# messages are still hidden, but are not silent!
|
||||
# command = "env G_MESSAGES_DEBUG=all SWAYNC_DEBUG=1 swaync";
|
||||
command = "swaync";
|
||||
command = "env G_MESSAGES_DEBUG=all swaync";
|
||||
readiness.waitDbus = "org.freedesktop.Notifications";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
# - SWAYNC_SUMMARY
|
||||
|
||||
# rules to use for testing. trigger with:
|
||||
# - `notify-send --app-name=foo subject body` (etc)
|
||||
# - `notify-send --app-id=foo subject body` (etc)
|
||||
# should also be possible to trigger via any messaging app
|
||||
fbcli-test-im = {
|
||||
body = "test:message";
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user