Compare commits

...

112 Commits

Author SHA1 Message Date
bcd467e60e NOT FOR MERGE: geoclue: disable polkit write access to the modem
unfortunately, geoclue misinterprets that as thinking it doesn't have *read* access either

hence, though it does prevent geoclue from changing GPS settings, it actually causes geoclue to simply not even read GPS (nor GSM)
2024-06-24 01:03:56 +00:00
304c8f8e3e docs: eg25-control: elaborate on Almanac v.s. Ephemeris 2024-06-24 00:25:54 +00:00
5a09a2665b secrets: net: add new home wifi creds 2024-06-23 18:16:10 +00:00
209545fc41 refactor: split satellite program into own file
mainly so i have a place to document its GUI :)
2024-06-23 12:44:50 +00:00
1e12566207 moby: disable gpsd
1. i wasn't actually relying on it for anything. 2. it's 100k LoC which runs as root (?), in a totally un-secured systemd service.

i may be enabling something similar like this in the future, to prevent geoclue from deciding to disable the GPS. i'll probably be going with the simpler gps-share or gnss-share for that
2024-06-23 03:53:24 +00:00
9a53cbc833 docs: geoclue: link to forums, git, API docs 2024-06-23 03:53:07 +00:00
439bb5263f switchboard: sandbox 2024-06-22 03:43:23 +00:00
845dba3ca5 modules/vpn: fix deprecation warnings 2024-06-22 03:35:41 +00:00
5e7fe850ec blast-ugjka: 0.6.2 -> 0.7.0 2024-06-22 01:25:26 +00:00
832338488d firefox-extensions.ether-metamask: 11.16.12 -> 11.16.13 2024-06-22 01:24:58 +00:00
86ee95f607 uassets: intra-day bump 2024-06-22 01:24:46 +00:00
5f5e55c98b nixpkgs-wayland: 2024-06-20 -> 2024-06-21 2024-06-22 01:24:26 +00:00
7d59782005 nixpkgs: intra-day bump 2024-06-22 01:24:09 +00:00
62b541012b blast-ugjka: add an updateScript 2024-06-21 19:14:47 +00:00
514197a17f docs: mmcli: include notes for how to enable the gps 2024-06-21 19:11:07 +00:00
143bdf672b ship satellite program
really this only belongs on devices which have a gps unit (i.e. moby).
maybe i'll tune that in the future.
2024-06-21 19:09:43 +00:00
a6c48eda71 geoclue2: remove extraneous appConfig where-am-i whitelisting
it's included by the default nixos service (which whitelists 'geoclue-demo-agent'
2024-06-21 17:49:15 +00:00
a603c3e6bc ols: synthesize cell tower data when no exact match is found 2024-06-21 16:24:13 +00:00
1f48f41927 ols: ship static cell tower position database from opencellid.org 2024-06-21 13:48:28 +00:00
c0d9f05575 python3Packages.ols: point to my own repo, 2023-06-15 -> 2024-06-21 2024-06-21 13:42:02 +00:00
7f46b034f9 opencellid: init at 0-unstable-2024-06-20 2024-06-21 12:55:38 +00:00
ba66378bc0 docs: ols: point to wigle docs and api limits 2024-06-21 11:54:38 +00:00
dcc8168aa0 nixpkgs: update sysvol patch 2024-06-07 -> 2024-06-20 2024-06-21 11:54:07 +00:00
f7d3c26d12 servo: irc: add wigle.net 2024-06-21 11:48:47 +00:00
3d871e8d7c scripts/check-nur: fix (linux-exynos5-mainline was invalid in default eval) 2024-06-21 07:43:33 +00:00
78f4cd9be2 sysvol: 2024-06-13 -> 2024-06-20 2024-06-21 07:25:15 +00:00
f83bac3c2b firefox-extensions -> latest 2024-06-21 07:25:15 +00:00
58de5d661f uassets: 2024-06-15 -> 2024-06-21 2024-06-21 07:25:15 +00:00
599832d59c 2024-06-11 -> 2024-06-16 2024-06-21 07:25:15 +00:00
625cb0992b nixpkgs-wayland: 2024-06-14 -> 2024-06-20 2024-06-21 07:25:15 +00:00
a02f221628 nixpkgs: 2024-06-19 -> 2024-06-21 2024-06-21 07:25:15 +00:00
ad8bcfc09e scripts/deploy: build all hosts before building all variants 2024-06-21 07:25:15 +00:00
815ce6287f scripts/update: fix that the script wasnt building the update scripts before trying to invoke them 2024-06-21 07:25:15 +00:00
0d1d56870f default.nix: copy everything to the nix store before evaluating any of my config 2024-06-21 07:25:15 +00:00
2445b882c2 scripts/update: more debug logging 2024-06-20 22:54:16 +00:00
12465e111e nixpkgs: update xsimd patch 2024-06-20 21:54:06 +00:00
65a0914828 fastcluster/ols: fix cross compilation to moby 2024-06-20 13:33:18 +00:00
dab60e79c1 moby: remove gps.nix 2024-06-20 12:01:10 +00:00
fe57f186cd gpsd: deploy via sane.programs interface 2024-06-20 11:57:09 +00:00
78d66a8b09 ols: pipe stderr > stdout 2024-06-20 11:52:35 +00:00
b2955c9c9d geoclue2: use nixpkgs' service 2024-06-20 11:52:13 +00:00
b0e184b0f0 geoclue: integrate ols/wigle as backend
the wigle API limits are *strict*. probably too strict to actually be useful
2024-06-20 11:35:26 +00:00
3cd97b522c ols: enable
still some more work to do to really integrate this with geoclue
2024-06-20 11:05:15 +00:00
c91681c77c python3Packages.ols: init at at 0.1.0-unstable-2023-06-15 2024-06-20 11:05:15 +00:00
d0d623da15 programs: ship geoclue2 as an actual program
moby probably needs its gps.nix file removed. also this is incomplete due to Mozilla terminating their location services API
2024-06-20 08:40:36 +00:00
0db86d8c86 moby: sway: use Alt as the modifier key 2024-06-20 05:55:12 +00:00
b74dfe7578 crappy: sway: use Alt as the mod key 2024-06-20 05:55:12 +00:00
d1843b6b3d refactor: sway-config -> config, to match its installed name 2024-06-20 05:55:12 +00:00
b482a1dfd6 swaync: disable debug, to un-break DND mode
i'm sick of G_MESSAGES_DEBUG breaking stuff. i must be parsing cli output that gets broken by that, somewhere
2024-06-20 05:24:53 +00:00
5ba74a4055 doc: swaync: fix typo in notify-send example 2024-06-20 05:17:51 +00:00
b3b77e3e62 nwg-panel: stylize with the goal that it uses a bit less space on moby 2024-06-20 02:39:45 +00:00
63bc58a56f playerctl: patch missing refcount tracking to avoid a crash in nwg-panel 2024-06-20 00:46:31 +00:00
efcf8639dc gdb: ship a python-capable gdbinit 2024-06-19 23:03:17 +00:00
90b86dc7fc servo: re-enable transmission and jackett 2024-06-19 21:29:32 +00:00
8bf8d31c5f nwg-panel: record the URL to the upstream PR 2024-06-19 21:28:18 +00:00
2e44abc55d scripts/deploy: fix off-by-one in runOnTarget 2024-06-19 21:05:37 +00:00
9e92069ba3 nwg-panel: migrate the mediaPrevNext patch to something suitable for upstream 2024-06-19 21:04:15 +00:00
2a592a4a15 nwg-panel: disable the music-note icon in playerctl 2024-06-19 20:35:50 +00:00
8ca357ea7f scripts/deploy: add a --reboot option 2024-06-19 20:31:43 +00:00
4f4c05a922 nwg-panel: fix playerctl patch for newest nwg-panel, and also hide the label
hopefully the label is the part which includes the music note icon
2024-06-19 19:40:23 +00:00
7c4be0f4e9 hosts: fix that -min and -light variants were actually identical to the full versions 2024-06-19 11:25:30 +00:00
afea7fe5e7 scripts/deploy: implement a dry-run mode 2024-06-19 11:24:33 +00:00
294f0061bd sxmo-utils: add a deprecation warning 2024-06-19 11:20:49 +00:00
4efe159933 nixpkgs: 2024-06-15 -> 2024-06-19 2024-06-19 10:35:58 +00:00
b7f99c022b nwg-panel: enable sysload by default 2024-06-19 02:04:33 +00:00
b3c5e53156 sane-sysload: implement CPU measurement 2024-06-19 01:58:21 +00:00
91c2b04ab4 sane-sysload: make the format CLI args friendlier 2024-06-19 01:20:36 +00:00
27efb10a27 refactor: rename sane-sysinfo -> sane-sysload 2024-06-19 01:04:06 +00:00
e4e32f46fe nwg-panel: integrate optional sysload executor (WIP; disabled by default) 2024-06-19 01:01:03 +00:00
64b169069a nwg-panel: fix issue that playerctl was being pushed off the bar 2024-06-19 00:47:43 +00:00
c2c15e1ac3 networkmanager_dmenu: sandbox 2024-06-19 00:44:35 +00:00
0b3156c4c7 nmcli: sandbox 2024-06-19 00:44:24 +00:00
1c8551c842 rofi: add a shortcut for configuring WiFi 2024-06-18 23:44:21 +00:00
2755d98b99 sane-open: make sure networkmanager_dmenu shows the keyboard when activated 2024-06-18 23:44:21 +00:00
543108a5dd networkmanager_dmenu: ship 2024-06-18 23:44:21 +00:00
b32d02dc3f programs: add pidof from procps
used by things like networkmanager-dmenu, which i plan to add soon
2024-06-18 23:44:20 +00:00
0bd92ef77e swaync: make the backlight/mpris items configurable 2024-06-18 21:41:15 +00:00
a7df4cc125 lint: swaync: sort config properties 2024-06-18 21:34:04 +00:00
09a615ee62 netns: factor the netns setup/teardown into distinct services, rather than trying to piggyback network-local-commands
idk what network-local-commands is about, nor network-pre.target.
network-pre.target doesn't seem to actually be wanted by anything (?)
2024-06-18 10:36:08 +00:00
8523b406ad todo.md: note that swaync brightness slider does not work 2024-06-18 09:48:35 +00:00
6021da072c replace builtins.toJSON with writers.writeJSON where possible
it gives pretty-printed JSON that's easier to work with
2024-06-18 09:33:17 +00:00
a49abbd123 hosts: add pubkeys for $host-hn 2024-06-18 09:33:17 +00:00
f9091c0b0c netns: ensure that network.target depends on network-pre.target (why doesnt it by default?)
this should fix that servo tries to start wg-ovpns before the netns is configured
2024-06-18 09:07:40 +00:00
bbf8fd5b20 servo: disable almost all WAN stuff (leave only wireguard, as a fallback) 2024-06-18 09:04:13 +00:00
be84747ffc check-uninsane: test FTP over doof (and simplify) 2024-06-18 08:09:06 +00:00
478b443430 doc: sftpgo: note that "/README.md" doesnt work 2024-06-18 07:56:26 +00:00
ded5f6560d check-uninsane: fix ftp test to work when invoked from the wan 2024-06-18 07:55:11 +00:00
c1b3629dcf swaync: fix segfault under load 2024-06-18 07:52:02 +00:00
5879499924 swaync-service-dispatcher: simplify quoting 2024-06-18 06:40:44 +00:00
5a63f294c0 servo: sftpgo: allow fully-anonymous www read access to /pub
this will help me write automated tests for its availability
2024-06-18 05:44:20 +00:00
891a29feeb nwg-panel: fix commands (like lock, shutdown) to not run through swaymsg 2024-06-18 02:51:58 +00:00
0863505877 wifi: add new network 2024-06-18 02:34:27 +00:00
0c922bd63a nwg-panel: fix reboot/shutdown commands to not assume systemd 2024-06-18 02:32:04 +00:00
e04ec4c706 nwg-panel: fix the "lock" button to use the correct screen locker 2024-06-18 02:31:46 +00:00
b0f9733ac8 sway: fix that Super+L didnt have sandbox access to start the screen locker 2024-06-18 02:26:57 +00:00
e2babfc076 nwg-panel: fix clock to always be centered, even if that means overlapped rendering 2024-06-18 01:08:02 +00:00
ef29b569e5 nwg-panel: fix clock size to actually be larger on lappy/desko 2024-06-18 01:07:43 +00:00
6f0a455d0b scripts/check-uninsane: implement --verbose flag 2024-06-17 23:36:52 +00:00
7d6a420c52 sane-vpn: allow sane-vpn do -- [COMMAND] 2024-06-17 23:26:28 +00:00
259143b87e scripts/check-uninsane: fix so ssh servo systemctl --failed actually works on servo, even when ssh key is locked 2024-06-17 23:24:34 +00:00
fce426c318 servo: trust-dns: expose the hn DNS server on port 53
nothing i had was *expecting* it to be on port 1053, and it was just never working (?)
2024-06-17 23:16:00 +00:00
9b794777b5 servo: trust-dns: have the ovpns DNS provider return doof-based addresses instead of WAN-based addresses 2024-06-17 23:14:21 +00:00
3ada668366 servo: expose all wan services also to the doof tunnel 2024-06-17 23:08:08 +00:00
39a39e763d trust-dns: hack to substitute ANATIVE before anything else 2024-06-17 22:44:43 +00:00
50353280d3 servo: port ANATIVE over the doof interface to return the doof IP address 2024-06-17 22:37:50 +00:00
72b8211029 servo: switch ns2.uninsane.org from ovpns -> doof 2024-06-17 22:19:36 +00:00
dbf719b59b scripts/check-uninsane: fix the git.uninsane.org check to work even w/o access to SSH keys 2024-06-17 22:16:23 +00:00
57d7d3821f scripts/check-uninsane: dont rely on ovpns.uninsane.org bootstrap DNS: directly test the known IPv4 address 2024-06-17 22:12:34 +00:00
e86e9fc079 scripts/check-uninsane: add a check for DNS via doofnet 2024-06-17 22:11:02 +00:00
d708b78ebe scripts/check-uninsane: fix ftp check, to not error if README.md exists in the current directory 2024-06-17 22:10:47 +00:00
075418eda1 git: add alias git com to commit 2024-06-17 22:01:58 +00:00
9fc5b83b61 refactor: servo: hardcode OVPN-related IP addresses in far fewer places 2024-06-17 22:00:39 +00:00
96 changed files with 1515 additions and 551 deletions

11
TODO.md
View File

@@ -15,7 +15,7 @@
- decrease s6 restart time? - decrease s6 restart time?
- `ssh` access doesn't grant same linux capabilities as login - `ssh` access doesn't grant same linux capabilities as login
- ringer (i.e. dino incoming call) doesn't prevent moby from sleeping - ringer (i.e. dino incoming call) doesn't prevent moby from sleeping
- sysvol (volume overlay): when casting with `blast`, sysvol doesn't react to volume changes - syshud (volume overlay): when casting with `blast`, syshud doesn't react to volume changes
- moby: kaslr is effectively disabled - moby: kaslr is effectively disabled
- `dmesg | grep "KASLR disabled due to lack of seed"` - `dmesg | grep "KASLR disabled due to lack of seed"`
- fix by adding `kaslrseed` to uboot script before `booti` - fix by adding `kaslrseed` to uboot script before `booti`
@@ -26,6 +26,11 @@
- `dmesg | grep 'hid_bpf: error while preloading HID BPF dispatcher: -22'` - `dmesg | grep 'hid_bpf: error while preloading HID BPF dispatcher: -22'`
- `s6` is not re-entrant - `s6` is not re-entrant
- so if the desktop crashes, the login process from `unl0kr` fails to re-launch the GUI - 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: ## REFACTORING:
- add import checks to my Python nix-shell scripts - add import checks to my Python nix-shell scripts
@@ -126,11 +131,9 @@
- add option to change audio output - add option to change audio output
- fix colors (red alert) to match overall theme - fix colors (red alert) to match overall theme
- moby: tune GPS - moby: tune GPS
- run only geoclue, and not gpsd, to save power?
- tune QGPS setting in eg25-control, for less jitter? - tune QGPS setting in eg25-control, for less jitter?
- direct mepo to prefer gpsd, with fallback to geoclue, for better accuracy?
- configure geoclue to do some smoothing? - configure geoclue to do some smoothing?
- manually do smoothing, as some layer between mepo and geoclue/gpsd? - manually do smoothing, as some layer between mepo and geoclue?
- moby: port `freshen-agps` timer service to s6 (maybe i want some `s6-cron` or something) - moby: port `freshen-agps` timer service to s6 (maybe i want some `s6-cron` or something)
- moby: show battery state on ssh login - moby: show battery state on ssh login
- moby: improve gPodder launch time - moby: improve gPodder launch time

View File

@@ -1,67 +1,5 @@
# limited, non-flake interface to this repo. { ... }@args:
# 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 let
mkPkgs = args: (import ./pkgs/additional/nixpkgs args).extend sane-nix-files = import ./pkgs/additional/sane-nix-files { };
(import ./overlays/all.nix); in
inherit (mkPkgs {}) lib; import "${sane-nix-files}/impure.nix" args
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 {}) // {
config = host.config;
fs = host.config.sane.fs;
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: {
# TODO: swap order: $host-{next,staging}-{min,light}:
# then lexicographically-adjacent targets would also have the minimal difference in closure,
# and the order in which each target should be built is more evident
"${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 {
inherit hosts;
} // (mkPkgs {})

View File

@@ -25,6 +25,7 @@
sane.programs.dino.config.autostart = false; sane.programs.dino.config.autostart = false;
sane.programs.dissent.config.autostart = false; sane.programs.dissent.config.autostart = false;
sane.programs.fractal.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; # sane.programs.guiApps.enableFor.user.colin = false;

View File

@@ -10,7 +10,6 @@
{ {
imports = [ imports = [
./fs.nix ./fs.nix
./gps.nix
]; ];
sane.hal.pine64.enable = true; sane.hal.pine64.enable = true;
@@ -29,6 +28,7 @@
sops.secrets.colin-passwd.neededForUsers = true; sops.secrets.colin-passwd.neededForUsers = true;
sane.programs.sway.enableFor.user.colin = 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.blueberry.enableFor.user.colin = false; # bluetooth manager: doesn't cross compile!
sane.programs.fcitx5.enableFor.user.colin = false; # does not cross compile sane.programs.fcitx5.enableFor.user.colin = false; # does not cross compile
sane.programs.mercurial.enableFor.user.colin = false; # does not cross compile sane.programs.mercurial.enableFor.user.colin = false; # does not cross compile

View File

@@ -1,68 +0,0 @@
# 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;
}

View File

@@ -36,7 +36,8 @@
# - rb = received bytes # - rb = received bytes
# - sp = sent packets # - sp = sent packets
# - sb = sent bytes # - sb = sent bytes
{ lib, ... }:
{ config, lib, ... }:
let let
# TURN port range (inclusive). # TURN port range (inclusive).
# default coturn behavior is to use the upper quarter of all ports. i.e. 49152 - 65535. # default coturn behavior is to use the upper quarter of all ports. i.e. 49152 - 65535.
@@ -130,11 +131,11 @@ in
"verbose" "verbose"
# "Verbose" #< even MORE verbosity than "verbose" (it's TOO MUCH verbosity really) # "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) "no-multicast-peers" # disables sending to IPv4 broadcast addresses (e.g. 224.0.0.0/3)
# "listening-ip=10.0.1.5" "external-ip=185.157.162.178" #< 2024/04/25: works, if running in root namespace # "listening-ip=${config.sane.netns.ovpns.hostVethIpv4}" "external-ip=${config.sane.netns.ovpns.netnsPubIpv4}" #< 2024/04/25: works, if running in root namespace
"listening-ip=185.157.162.178" "external-ip=185.157.162.178" "listening-ip=${config.sane.netns.ovpns.netnsPubIpv4}" "external-ip=${config.sane.netns.ovpns.netnsPubIpv4}"
# old attempts: # old attempts:
# "external-ip=185.157.162.178/10.0.1.5" # "external-ip=${config.sane.netns.ovpns.netnsPubIpv4}/${config.sane.netns.ovpns.hostVethIpv4}"
# "listening-ip=10.78.79.51" # can be specified multiple times; omit for * # "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/10.78.79.51"
# "external-ip=97.113.128.229" # "external-ip=97.113.128.229"

View File

@@ -51,54 +51,54 @@ lib.mkIf false
{ {
"3478" = { "3478" = {
protocol = [ "tcp" "udp" ]; protocol = [ "tcp" "udp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-stun-turn"; description = "colin-xmpp-stun-turn";
}; };
"5222" = { "5222" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-client-to-server"; description = "colin-xmpp-client-to-server";
}; };
"5223" = { "5223" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpps-client-to-server"; # XMPP over TLS description = "colin-xmpps-client-to-server"; # XMPP over TLS
}; };
"5269" = { "5269" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.wan = true; visibleTo.doof = true;
description = "colin-xmpp-server-to-server"; description = "colin-xmpp-server-to-server";
}; };
"5270" = { "5270" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.wan = true; visibleTo.doof = true;
description = "colin-xmpps-server-to-server"; # XMPP over TLS description = "colin-xmpps-server-to-server"; # XMPP over TLS
}; };
"5280" = { "5280" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-bosh"; description = "colin-xmpp-bosh";
}; };
"5281" = { "5281" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-bosh-https"; description = "colin-xmpp-bosh-https";
}; };
"5349" = { "5349" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-stun-turn-over-tls"; description = "colin-xmpp-stun-turn-over-tls";
}; };
"5443" = { "5443" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-web-services"; # file uploads, websockets, admin description = "colin-xmpp-web-services"; # file uploads, websockets, admin
}; };
} }
@@ -109,8 +109,8 @@ lib.mkIf false
numPorts = turnPortHigh - turnPortLow + 1; numPorts = turnPortHigh - turnPortLow + 1;
in { in {
protocol = [ "tcp" "udp" ]; protocol = [ "tcp" "udp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-turn-${builtins.toString count}-of-${builtins.toString numPorts}"; description = "colin-xmpp-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
}; };
}) })

View File

@@ -8,14 +8,14 @@
{ {
sane.ports.ports."143" = { sane.ports.ports."143" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-imap-imap.uninsane.org"; description = "colin-imap-imap.uninsane.org";
}; };
sane.ports.ports."993" = { sane.ports.ports."993" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-imaps-imap.uninsane.org"; description = "colin-imaps-imap.uninsane.org";
}; };

View File

@@ -1,6 +1,6 @@
# postfix config options: <https://www.postfix.org/postconf.5.html> # postfix config options: <https://www.postfix.org/postconf.5.html>
{ lib, pkgs, ... }: { config, lib, pkgs, ... }:
let let
submissionOptions = { submissionOptions = {
@@ -56,8 +56,7 @@ in
sane.dns.zones."uninsane.org".inet = { sane.dns.zones."uninsane.org".inet = {
MX."@" = "10 mx.uninsane.org."; MX."@" = "10 mx.uninsane.org.";
# XXX: RFC's specify that the MX record CANNOT BE A CNAME A."mx" = "%AOVPNS%"; #< XXX: RFC's specify that the MX record CANNOT BE A CNAME. TODO: use "%AOVPNS%?
A."mx" = "185.157.162.178";
# Sender Policy Framework: # Sender Policy Framework:
# +mx => mail passes if it originated from the MX # +mx => mail passes if it originated from the MX

View File

@@ -12,6 +12,10 @@
device = "/var/media"; device = "/var/media";
options = [ "rbind" ]; options = [ "rbind" ];
}; };
fileSystems."/var/export/pub" = {
device = "/var/www/sites/uninsane.org/share";
options = [ "rbind" ];
};
# fileSystems."/var/export/playground" = { # fileSystems."/var/export/playground" = {
# device = config.fileSystems."/mnt/persist/ext".device; # device = config.fileSystems."/mnt/persist/ext".device;
# fsType = "btrfs"; # fsType = "btrfs";
@@ -37,7 +41,8 @@
wantedBy = [ "nfs.service" "sftpgo.service" ]; wantedBy = [ "nfs.service" "sftpgo.service" ];
file.text = '' file.text = ''
- media/ read-only: Videos, Music, Books, etc - media/ read-only: Videos, Music, Books, etc
- playground/ read-write: use it to share files with other users of this server - 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
''; '';
}; };

View File

@@ -27,13 +27,12 @@ in
"21" = { "21" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.lan = true; visibleTo.lan = true;
# visibleTo.wan = true;
description = "colin-FTP server"; description = "colin-FTP server";
}; };
"990" = { "990" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-FTPS server"; description = "colin-FTPS server";
}; };
} // (sane-lib.mapToAttrs } // (sane-lib.mapToAttrs
@@ -41,8 +40,8 @@ in
name = builtins.toString port; name = builtins.toString port;
value = { value = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-FTP server data port range"; description = "colin-FTP server data port range";
}; };
}) })
@@ -101,6 +100,13 @@ in
debug = true; debug = true;
tls_mode = 2; # 2 = "implicit FTPS": client negotiates TLS before any FTP command. 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 # active mode is susceptible to "bounce attacks", without much benefit over passive mode
@@ -117,7 +123,7 @@ in
banner = '' banner = ''
Welcome, friends, to Colin's FTP server! Also available via NFS on the same host, but LAN-only. Welcome, friends, to Colin's FTP server! Also available via NFS on the same host, but LAN-only.
Read-only access (LAN-restricted): Read-only access (LAN clients see everything; WAN clients can only see /pub):
Username: "anonymous" Username: "anonymous"
Password: "anonymous" Password: "anonymous"

View File

@@ -45,6 +45,8 @@ from hmac import compare_digest
authFail = dict(username="") authFail = dict(username="")
PERM_DENY = []
PERM_LIST = [ "list" ]
PERM_RO = [ "list", "download" ] PERM_RO = [ "list", "download" ]
PERM_RW = [ PERM_RW = [
# read-only: # read-only:
@@ -127,12 +129,14 @@ def getAuthResponse(ip: str, username: str, password: str) -> dict:
return mkAuthOk(username, permissions = { return mkAuthOk(username, permissions = {
"/": PERM_RW, "/": PERM_RW,
"/playground": PERM_RW, "/playground": PERM_RW,
"/pub": PERM_RO,
}) })
if isWireguard(ip): if isWireguard(ip):
# allow any user from wireguard # allow any user from wireguard
return mkAuthOk(username, permissions = { return mkAuthOk(username, permissions = {
"/": PERM_RW, "/": PERM_RW,
"/playground": PERM_RW, "/playground": PERM_RW,
"/pub": PERM_RO,
}) })
if isLan(ip): if isLan(ip):
if username == "anonymous": if username == "anonymous":
@@ -140,6 +144,18 @@ def getAuthResponse(ip: str, username: str, password: str) -> dict:
return mkAuthOk("anonymous", permissions = { return mkAuthOk("anonymous", permissions = {
"/": PERM_RO, "/": PERM_RO,
"/playground": PERM_RW, "/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 return authFail

View File

@@ -133,7 +133,7 @@
sane.ports.ports."22" = { sane.ports.ports."22" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true; visibleTo.doof = true;
description = "colin-git@git.uninsane.org"; description = "colin-git@git.uninsane.org";
}; };
} }

View File

@@ -1,6 +1,5 @@
{ lib, pkgs, ... }: { config, lib, pkgs, ... }:
lib.mkIf false #< TODO: re-enable once confident of sandboxing
{ {
sane.persist.sys.byStore.plaintext = [ sane.persist.sys.byStore.plaintext = [
# TODO: mode? we only need this to save Indexer creds ==> migrate to config? # TODO: mode? we only need this to save Indexer creds ==> migrate to config?
@@ -13,7 +12,7 @@ lib.mkIf false #< TODO: re-enable once confident of sandboxing
systemd.services.jackett.serviceConfig = { systemd.services.jackett.serviceConfig = {
# run this behind the OVPN static VPN # run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns"; NetworkNamespacePath = "/run/netns/ovpns";
ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect 185.157.162.178" ]; # abort if public IP is not as expected ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect ${config.sane.netns.ovpns.netnsPubIpv4}" ]; # abort if public IP is not as expected
# patch jackett to listen on the public interfaces # patch jackett to listen on the public interfaces
# ExecStart = lib.mkForce "${pkgs.jackett}/bin/Jackett --NoUpdates --DataFolder /var/lib/jackett/.config/Jackett --ListenPublic"; # ExecStart = lib.mkForce "${pkgs.jackett}/bin/Jackett --NoUpdates --DataFolder /var/lib/jackett/.config/Jackett --ListenPublic";
@@ -25,8 +24,7 @@ lib.mkIf false #< TODO: re-enable once confident of sandboxing
enableACME = true; enableACME = true;
# inherit kTLS; # inherit kTLS;
locations."/" = { locations."/" = {
# proxyPass = "http://ovpns.uninsane.org:9117"; proxyPass = "http://${config.sane.netns.ovpns.netnsVethIpv4}:9117";
proxyPass = "http://10.0.1.6:9117";
recommendedProxySettings = true; recommendedProxySettings = true;
}; };
}; };

View File

@@ -4,12 +4,11 @@
{ config, lib, ... }: { config, lib, ... }:
let let
ircServer = { name, additionalAddresses ? [], sasl ? true, port ? 6697 }: let ircServer = { name, additionalAddresses ? [], ssl ? true, sasl ? true, port ? if ssl then 6697 else 6667 }: let
lowerName = lib.toLower name; lowerName = lib.toLower name;
in { in {
# XXX sasl: appservice doesn't support NickServ identification (only SASL, or PASS if sasl = false) # XXX sasl: appservice doesn't support NickServ identification (only SASL, or PASS if sasl = false)
inherit name additionalAddresses sasl port; inherit additionalAddresses name port sasl ssl;
ssl = true;
botConfig = { botConfig = {
# bot has no presence in IRC channel; only real Matrix users # bot has no presence in IRC channel; only real Matrix users
enabled = false; enabled = false;
@@ -156,6 +155,10 @@ in
# - #sxmo-offtopic # - #sxmo-offtopic
}; };
"irc.rizon.net" = ircServer { name = "Rizon"; }; "irc.rizon.net" = ircServer { name = "Rizon"; };
"wigle.net" = ircServer {
name = "WiGLE";
ssl = false;
};
}; };
}; };
}; };

View File

@@ -17,7 +17,6 @@ in
sane.ports.ports."80" = { sane.ports.ports."80" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
visibleTo.ovpns = true; # so that letsencrypt can procure a cert for the mx record visibleTo.ovpns = true; # so that letsencrypt can procure a cert for the mx record
visibleTo.doof = true; visibleTo.doof = true;
description = "colin-http-uninsane.org"; description = "colin-http-uninsane.org";
@@ -25,7 +24,6 @@ in
sane.ports.ports."443" = { sane.ports.ports."443" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
visibleTo.doof = true; visibleTo.doof = true;
description = "colin-https-uninsane.org"; description = "colin-https-uninsane.org";
}; };

View File

@@ -86,7 +86,7 @@ in
sane.ports.ports."${builtins.toString altPort}" = { sane.ports.ports."${builtins.toString altPort}" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true; visibleTo.doof = true;
description = "colin-ntfy.uninsane.org"; description = "colin-ntfy.uninsane.org";
}; };
} }

View File

@@ -62,8 +62,8 @@ in
sane.ports.ports = lib.mkMerge (lib.forEach portRange (port: { sane.ports.ports = lib.mkMerge (lib.forEach portRange (port: {
"${builtins.toString port}" = { "${builtins.toString port}" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-notification-waiter-${builtins.toString (port - portLow + 1)}-of-${builtins.toString numPorts}"; description = "colin-notification-waiter-${builtins.toString (port - portLow + 1)}-of-${builtins.toString numPorts}";
}; };
})); }));

View File

@@ -61,42 +61,42 @@ in
]; ];
sane.ports.ports."5000" = { sane.ports.ports."5000" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-prosody-fileshare-proxy65"; description = "colin-xmpp-prosody-fileshare-proxy65";
}; };
sane.ports.ports."5222" = { sane.ports.ports."5222" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-client-to-server"; description = "colin-xmpp-client-to-server";
}; };
sane.ports.ports."5223" = { sane.ports.ports."5223" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpps-client-to-server"; # XMPP over TLS description = "colin-xmpps-client-to-server"; # XMPP over TLS
}; };
sane.ports.ports."5269" = { sane.ports.ports."5269" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.wan = true; visibleTo.doof = true;
description = "colin-xmpp-server-to-server"; description = "colin-xmpp-server-to-server";
}; };
sane.ports.ports."5270" = { sane.ports.ports."5270" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.wan = true; visibleTo.doof = true;
description = "colin-xmpps-server-to-server"; # XMPP over TLS description = "colin-xmpps-server-to-server"; # XMPP over TLS
}; };
sane.ports.ports."5280" = { sane.ports.ports."5280" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-bosh"; description = "colin-xmpp-bosh";
}; };
sane.ports.ports."5281" = { sane.ports.ports."5281" = {
protocol = [ "tcp" ]; protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-prosody-https"; # necessary? description = "colin-xmpp-prosody-https"; # necessary?
}; };

View File

@@ -32,7 +32,7 @@
forceSSL = true; forceSSL = true;
enableACME = true; enableACME = true;
locations."/" = { locations."/" = {
proxyPass = "http://10.0.1.6:5030"; proxyPass = "http://${config.sane.netns.ovpns.netnsVethIpv4}:5030";
proxyWebsockets = true; proxyWebsockets = true;
}; };
}; };
@@ -71,7 +71,7 @@
systemd.services.slskd.serviceConfig = { systemd.services.slskd.serviceConfig = {
# run this behind the OVPN static VPN # run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns"; NetworkNamespacePath = "/run/netns/ovpns";
ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect 185.157.162.178" ]; # abort if public IP is not as expected ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect ${config.sane.netns.ovpns.netnsPubIpv4}" ]; # abort if public IP is not as expected
Restart = lib.mkForce "always"; # exits "success" when it fails to connect to soulseek server Restart = lib.mkForce "always"; # exits "success" when it fails to connect to soulseek server
RestartSec = "60s"; RestartSec = "60s";

View File

@@ -82,7 +82,6 @@ let
''; '';
}; };
in in
lib.mkIf false #< TODO: re-enable once confident of sandboxing
{ {
sane.persist.sys.byStore.plaintext = [ sane.persist.sys.byStore.plaintext = [
# TODO: mode? we need this specifically for the stats tracking in .config/ # TODO: mode? we need this specifically for the stats tracking in .config/
@@ -106,8 +105,8 @@ lib.mkIf false #< TODO: re-enable once confident of sandboxing
# DOCUMENTATION/options list: <https://github.com/transmission/transmission/blob/main/docs/Editing-Configuration-Files.md#options> # 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. # message-level = 3; #< enable for debug logging. 0-3, default is 2.
# 10.0.1.6 => allow rpc only from the root servo ns. it'll tunnel things to the net, if need be. # ovpns.netnsVethIpv4 => 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-bind-address = config.sane.netns.ovpns.netnsVethIpv4;
#rpc-host-whitelist = "bt.uninsane.org"; #rpc-host-whitelist = "bt.uninsane.org";
#rpc-whitelist = "*.*.*.*"; #rpc-whitelist = "*.*.*.*";
rpc-authentication-required = true; rpc-authentication-required = true;
@@ -118,7 +117,7 @@ lib.mkIf false #< TODO: re-enable once confident of sandboxing
rpc-whitelist-enabled = false; rpc-whitelist-enabled = false;
# force behind ovpns in case the NetworkNamespace fails somehow # force behind ovpns in case the NetworkNamespace fails somehow
bind-address-ipv4 = "185.157.162.178"; bind-address-ipv4 = config.sane.netns.ovpns.netnsPubIpv4;
port-forwarding-enabled = false; port-forwarding-enabled = false;
# hopefully, make the downloads world-readable # hopefully, make the downloads world-readable
@@ -160,7 +159,7 @@ lib.mkIf false #< TODO: re-enable once confident of sandboxing
systemd.services.transmission.serviceConfig = { systemd.services.transmission.serviceConfig = {
# run this behind the OVPN static VPN # run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns"; NetworkNamespacePath = "/run/netns/ovpns";
ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect 185.157.162.178" ]; # abort if public IP is not as expected ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect ${config.sane.netns.ovpns.netnsPubIpv4}" ]; # abort if public IP is not as expected
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "30s"; RestartSec = "30s";
@@ -190,7 +189,7 @@ lib.mkIf false #< TODO: re-enable once confident of sandboxing
# inherit kTLS; # inherit kTLS;
locations."/" = { locations."/" = {
# proxyPass = "http://ovpns.uninsane.org:9091"; # proxyPass = "http://ovpns.uninsane.org:9091";
proxyPass = "http://10.0.1.6:9091"; proxyPass = "http://${config.sane.netns.ovpns.netnsVethIpv4}:9091";
}; };
}; };

View File

@@ -4,14 +4,12 @@
let let
dyn-dns = config.sane.services.dyn-dns; dyn-dns = config.sane.services.dyn-dns;
nativeAddrs = lib.mapAttrs (_name: builtins.head) config.sane.dns.zones."uninsane.org".inet.A; nativeAddrs = lib.mapAttrs (_name: builtins.head) config.sane.dns.zones."uninsane.org".inet.A;
bindOvpn = "10.0.1.5";
bindDoof = "10.0.2.5";
in in
{ {
sane.ports.ports."53" = { sane.ports.ports."53" = {
protocol = [ "udp" "tcp" ]; protocol = [ "udp" "tcp" ];
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = true; # visibleTo.wan = true;
visibleTo.ovpns = true; visibleTo.ovpns = true;
visibleTo.doof = true; visibleTo.doof = true;
description = "colin-dns-hosting"; description = "colin-dns-hosting";
@@ -41,6 +39,7 @@ in
CNAME."native" = "%CNAMENATIVE%"; CNAME."native" = "%CNAMENATIVE%";
A."@" = "%ANATIVE%"; A."@" = "%ANATIVE%";
A."servo.wan" = "%AWAN%"; A."servo.wan" = "%AWAN%";
A."servo.doof" = "%ADOOF%";
A."servo.lan" = config.sane.hosts.by-name."servo".lan-ip; A."servo.lan" = config.sane.hosts.by-name."servo".lan-ip;
A."servo.hn" = config.sane.hosts.by-name."servo".wg-home.ip; A."servo.hn" = config.sane.hosts.by-name."servo".wg-home.ip;
@@ -48,9 +47,9 @@ in
# it's best that we keep this identical, or a superset of, what org. lists as our NS. # it's best that we keep this identical, or a superset of, what org. lists as our NS.
# so, org. can specify ns2/ns3 as being to the VPN, with no mention of ns1. we provide ns1 here. # so, org. can specify ns2/ns3 as being to the VPN, with no mention of ns1. we provide ns1 here.
A."ns1" = "%ANATIVE%"; A."ns1" = "%ANATIVE%";
A."ns2" = "185.157.162.178"; A."ns2" = "%ADOOF%";
A."ns3" = "185.157.162.178"; A."ns3" = "%AOVPNS%";
A."ovpns" = "185.157.162.178"; A."ovpns" = "%AOVPNS%";
NS."@" = [ NS."@" = [
"ns1.uninsane.org." "ns1.uninsane.org."
"ns2.uninsane.org." "ns2.uninsane.org."
@@ -61,59 +60,64 @@ in
services.trust-dns.settings.zones = [ "uninsane.org" ]; services.trust-dns.settings.zones = [ "uninsane.org" ];
networking.nat.enable = true; networking.nat.enable = true; #< TODO: try removing this?
networking.nat.extraCommands = '' # networking.nat.extraCommands = ''
# redirect incoming DNS requests from LAN addresses # # redirect incoming DNS requests from LAN addresses
# to the LAN-specialized DNS service # # to the LAN-specialized DNS service
# N.B.: use the `nixos-*` chains instead of e.g. PREROUTING # # N.B.: use the `nixos-*` chains instead of e.g. PREROUTING
# because they get cleanly reset across activations or `systemctl restart firewall` # # because they get cleanly reset across activations or `systemctl restart firewall`
# instead of accumulating cruft # # instead of accumulating cruft
iptables -t nat -A nixos-nat-pre -p udp --dport 53 \ # iptables -t nat -A nixos-nat-pre -p udp --dport 53 \
-m iprange --src-range 10.78.76.0-10.78.79.255 \ # -m iprange --src-range 10.78.76.0-10.78.79.255 \
-j DNAT --to-destination :1053 # -j DNAT --to-destination :1053
iptables -t nat -A nixos-nat-pre -p tcp --dport 53 \ # iptables -t nat -A nixos-nat-pre -p tcp --dport 53 \
-m iprange --src-range 10.78.76.0-10.78.79.255 \ # -m iprange --src-range 10.78.76.0-10.78.79.255 \
-j DNAT --to-destination :1053 # -j DNAT --to-destination :1053
''; # '';
sane.ports.ports."1053" = { # sane.ports.ports."1053" = {
# because the NAT above redirects in nixos-nat-pre, LAN requests behave as though they arrived on the external interface at the redirected port. # # because the NAT above redirects in nixos-nat-pre, LAN requests behave as though they arrived on the external interface at the redirected port.
# TODO: try nixos-nat-post instead? # # TODO: try nixos-nat-post instead?
# TODO: or, don't NAT from port 53 -> port 1053, but rather nat from LAN addr to a loopback addr. # # 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 # # - this is complicated in that loopback is a different interface than eth0, so rewriting the destination address would cause the packets to just be dropped by the interface
protocol = [ "udp" "tcp" ]; # protocol = [ "udp" "tcp" ];
visibleTo.lan = true; # visibleTo.lan = true;
description = "colin-redirected-dns-for-lan-namespace"; # description = "colin-redirected-dns-for-lan-namespace";
}; # };
sane.services.trust-dns.enable = true; sane.services.trust-dns.enable = true;
sane.services.trust-dns.instances = let sane.services.trust-dns.instances = let
mkSubstitutions = flavor: { mkSubstitutions = flavor: {
"%ADOOF%" = config.sane.netns.doof.netnsPubIpv4;
"%ANATIVE%" = nativeAddrs."servo.${flavor}";
"%AOVPNS%" = config.sane.netns.ovpns.netnsPubIpv4;
"%AWAN%" = "$(cat '${dyn-dns.ipPath}')"; "%AWAN%" = "$(cat '${dyn-dns.ipPath}')";
"%CNAMENATIVE%" = "servo.${flavor}"; "%CNAMENATIVE%" = "servo.${flavor}";
"%ANATIVE%" = nativeAddrs."servo.${flavor}";
"%AOVPNS%" = "185.157.162.178";
}; };
in in
{ {
wan = { doof = {
substitutions = mkSubstitutions "wan"; substitutions = mkSubstitutions "doof";
listenAddrsIpv4 = [ listenAddrsIpv4 = [
nativeAddrs."servo.lan" config.sane.netns.doof.hostVethIpv4
bindOvpn config.sane.netns.ovpns.hostVethIpv4
bindDoof
]; ];
}; };
lan = {
substitutions = mkSubstitutions "lan";
listenAddrsIpv4 = [ nativeAddrs."servo.lan" ];
port = 1053;
};
hn = { hn = {
substitutions = mkSubstitutions "hn"; substitutions = mkSubstitutions "hn";
listenAddrsIpv4 = [ nativeAddrs."servo.hn" ]; listenAddrsIpv4 = [ nativeAddrs."servo.hn" ];
port = 1053;
}; };
lan = {
substitutions = mkSubstitutions "lan";
listenAddrsIpv4 = [ nativeAddrs."servo.lan" ];
# port = 1053;
};
# wan = {
# substitutions = mkSubstitutions "wan";
# listenAddrsIpv4 = [
# nativeAddrs."servo.lan"
# ];
# };
# hn-resolver = { # hn-resolver = {
# # don't need %AWAN% here because we forward to the hn instance. # # don't need %AWAN% here because we forward to the hn instance.
# listenAddrsIpv4 = [ nativeAddrs."servo.hn" ]; # listenAddrsIpv4 = [ nativeAddrs."servo.hn" ];
@@ -154,9 +158,10 @@ in
}; };
sane.services.dyn-dns.restartOnChange = [ sane.services.dyn-dns.restartOnChange = [
"trust-dns-wan.service" "trust-dns-doof.service"
"trust-dns-lan.service"
"trust-dns-hn.service" "trust-dns-hn.service"
"trust-dns-lan.service"
# "trust-dns-wan.service"
# "trust-dns-hn-resolver.service" # doesn't need restart because it doesn't know about WAN IP # "trust-dns-hn-resolver.service" # doesn't need restart because it doesn't know about WAN IP
]; ];
} }

View File

@@ -574,10 +574,6 @@ in
gawk.sandbox.wrapperType = "inplace"; # /share/gawk libraries refer to /libexec gawk.sandbox.wrapperType = "inplace"; # /share/gawk libraries refer to /libexec
gawk.sandbox.autodetectCliPaths = "existingFile"; 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 = {}; geoclue2-with-demo-agent = {};
# MS GitHub stores auth token in .config # MS GitHub stores auth token in .config
@@ -1181,13 +1177,12 @@ in
]; ];
}; };
hardware.opengl = lib.mkIf config.sane.programs.guiApps.enabled ({ hardware.graphics = lib.mkIf config.sane.programs.guiApps.enabled ({
enable = true; enable = true;
driSupport = lib.mkDefault true;
} // (lib.optionalAttrs pkgs.stdenv.isx86_64 { } // (lib.optionalAttrs pkgs.stdenv.isx86_64 {
# for 32 bit applications # for 32 bit applications
# upstream nixpkgs forbids setting driSupport32Bit unless specifically x86_64 (so aarch64 isn't allowed) # upstream nixpkgs forbids setting enable32Bit unless specifically x86_64 (so aarch64 isn't allowed)
driSupport32Bit = lib.mkDefault true; enable32Bit = lib.mkDefault true;
})); }));
system.activationScripts.notifyActive = lib.mkIf config.sane.programs.guiApps.enabled { system.activationScripts.notifyActive = lib.mkIf config.sane.programs.guiApps.enabled {

View File

@@ -111,7 +111,7 @@ in
''; '';
}); });
fs.".config/bonsai/bonsai_tree.json".symlink.text = builtins.toJSON cfg.config.transitions; fs.".config/bonsai/bonsai_tree.json".symlink.target = pkgs.writers.writeJSON "bonsai_tree.json" cfg.config.transitions;
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.extraRuntimePaths = [ sandbox.extraRuntimePaths = [

View File

@@ -66,6 +66,7 @@ end
if vars.percent ~= nil then if vars.percent ~= nil then
bat_args = bat_args .. " --percent-suffix '" .. vars.percent .. "'" bat_args = bat_args .. " --percent-suffix '" .. vars.percent .. "'"
end end
bat_args = bat_args .. " {bat}"
-- N.B.: `[[ <text> ]]` is Lua's multiline string literal -- N.B.: `[[ <text> ]]` is Lua's multiline string literal
conky.text = [[ conky.text = [[
@@ -73,8 +74,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} ${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 @bat@ ]] .. bat_args .. [[ }${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 @weather@ }${font} ${color1}${shadecolor}${font sans-serif:size=20:style=Bold}${alignc}${texeci 600 timeout 20 sane-weather }${font}
${color2}${shadecolor a4d7d0}${font sans-serif:size=16}${alignc}⇅ ${downspeedf wlan0}]] .. vars.kBps .. [[${font} ${color2}${shadecolor a4d7d0}${font sans-serif:size=16}${alignc}⇅ ${downspeedf wlan0}]] .. vars.kBps .. [[${font}

View File

@@ -5,22 +5,18 @@
sandbox.net = "clearnet"; #< for the scripts it calls (weather) sandbox.net = "clearnet"; #< for the scripts it calls (weather)
sandbox.extraPaths = [ sandbox.extraPaths = [
"/sys/class/power_supply" "/sys/class/power_supply"
"/sys/devices" # needed by sane-sysinfo "/sys/devices" # needed by sane-sysload
# "/sys/devices/cpu" # "/sys/devices/cpu"
# "/sys/devices/system" # "/sys/devices/system"
]; ];
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
suggestedPrograms = [ suggestedPrograms = [
"sane-sysinfo" "sane-sysload"
"sane-weather" "sane-weather"
]; ];
fs.".config/conky/conky.conf".symlink.target = pkgs.substituteAll { fs.".config/conky/conky.conf".symlink.target = ./conky.conf;
src = ./conky.conf;
bat = "sane-sysinfo";
weather = "timeout 20 sane-weather";
};
services.conky = { services.conky = {
description = "conky dynamic desktop background"; description = "conky dynamic desktop background";

View File

@@ -50,8 +50,11 @@
./fwupd.nix ./fwupd.nix
./g4music.nix ./g4music.nix
./gajim.nix ./gajim.nix
./gdb.nix
./gdbus.nix ./gdbus.nix
./geary.nix ./geary.nix
./geoclue-demo-agent.nix
./geoclue2.nix
./git.nix ./git.nix
./gnome-clocks.nix ./gnome-clocks.nix
./gnome-feeds.nix ./gnome-feeds.nix
@@ -60,6 +63,7 @@
./gnome-weather.nix ./gnome-weather.nix
./go2tv.nix ./go2tv.nix
./gpodder.nix ./gpodder.nix
./gpsd.nix
./grimshot.nix ./grimshot.nix
./gst-device-monitor.nix ./gst-device-monitor.nix
./gthumb.nix ./gthumb.nix
@@ -87,6 +91,7 @@
./msmtp.nix ./msmtp.nix
./nautilus.nix ./nautilus.nix
./neovim.nix ./neovim.nix
./networkmanager_dmenu
./newsflash.nix ./newsflash.nix
./nheko.nix ./nheko.nix
./nicotine-plus.nix ./nicotine-plus.nix
@@ -98,8 +103,10 @@
./objdump.nix ./objdump.nix
./obsidian.nix ./obsidian.nix
./offlineimap.nix ./offlineimap.nix
./ols.nix
./open-in-mpv.nix ./open-in-mpv.nix
./pactl.nix ./pactl.nix
./pidof.nix
./pipewire.nix ./pipewire.nix
./planify.nix ./planify.nix
./portfolio-filemanager.nix ./portfolio-filemanager.nix
@@ -114,9 +121,10 @@
./sane-open.nix ./sane-open.nix
./sane-screenshot.nix ./sane-screenshot.nix
./sane-scripts.nix ./sane-scripts.nix
./sane-sysinfo.nix ./sane-sysload.nix
./sane-theme.nix ./sane-theme.nix
./sanebox.nix ./sanebox.nix
./satellite.nix
./schlock.nix ./schlock.nix
./seatd.nix ./seatd.nix
./sfeed.nix ./sfeed.nix
@@ -136,7 +144,7 @@
./swaylock.nix ./swaylock.nix
./swaynotificationcenter ./swaynotificationcenter
./switchboard.nix ./switchboard.nix
./sysvol.nix ./syshud.nix
./tangram.nix ./tangram.nix
./tor-browser.nix ./tor-browser.nix
./tuba.nix ./tuba.nix
@@ -144,6 +152,7 @@
./vlc.nix ./vlc.nix
./waybar ./waybar
./waylock.nix ./waylock.nix
./where-am-i.nix
./wike.nix ./wike.nix
./wine.nix ./wine.nix
./wireplumber.nix ./wireplumber.nix

View File

@@ -55,7 +55,7 @@ in
# - theme-demo # - theme-demo
# - timeout-completed # - timeout-completed
# - window-close # - window-close
fs.".config/feedbackd/themes/proxied.json".symlink.text = builtins.toJSON { fs.".config/feedbackd/themes/proxied.json".symlink.target = pkgs.writers.writeJSON "proxied.json" {
name = "proxied"; name = "proxied";
parent-theme = "default"; parent-theme = "default";
profiles = [ profiles = [

View File

@@ -0,0 +1,13 @@
{ 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
'';
};
}

View File

@@ -0,0 +1,16 @@
{ 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";
}];
services.geoclue-agent = {
description = "geoclue 'demo' agent";
command = "geoclue-demo-agent";
partOf = [ "graphical-session" ];
};
};
}

View File

@@ -0,0 +1,62 @@
# 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 = [
"geoclue-demo-agent"
"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;
# prevent geoclue from modifying the GPS settings: i manage that myself, and trying to co-manage it with geoclue causes issues.
security.polkit.extraConfig = lib.optionalString cfg.enabled ''
polkit.addRule(function(action, subject) {
if (subject.user == "geoclue" && action.id == "org.freedesktop.ModemManager1.Device.Control") {
return polkit.Result.NO;
}
});
'';
services.geoclue2 = lib.mkIf cfg.enabled {
enable = true;
geoProviderUrl = "http://127.0.0.1:8088/v1/geolocate"; #< ols
};
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;
};
}

View File

@@ -40,6 +40,7 @@ in
alias.amend = "commit --amend --no-edit"; alias.amend = "commit --amend --no-edit";
alias.br = "branch"; alias.br = "branch";
alias.co = "checkout"; alias.co = "checkout";
alias.com = "commit";
alias.cp = "cherry-pick"; alias.cp = "cherry-pick";
alias.d = "difftool"; alias.d = "difftool";
alias.dif = "diff"; # common typo alias.dif = "diff"; # common typo

View File

@@ -1,12 +1,12 @@
# gnome feeds RSS viewer # gnome feeds RSS viewer
{ config, lib, sane-lib, ... }: { config, lib, pkgs, sane-lib, ... }:
let let
feeds = sane-lib.feeds; feeds = sane-lib.feeds;
all-feeds = config.sane.feeds; all-feeds = config.sane.feeds;
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds; wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
in { in {
sane.programs.gnome-feeds.fs.".config/org.gabmus.gfeeds.json".symlink.text = builtins.toJSON { sane.programs.gnome-feeds.fs.".config/org.gabmus.gfeeds.json".symlink.target = pkgs.writers.writeJSON "org.gabmus.gfeeds.json" {
# feed format is a map from URL to a dict, # feed format is a map from URL to a dict,
# with dict["tags"] a list of string tags. # with dict["tags"] a list of string tags.
feeds = sane-lib.mapToAttrs (feed: { feeds = sane-lib.mapToAttrs (feed: {

View File

@@ -1,7 +1,14 @@
# SUPPORT:
# - irc: #gnome-maps on irc.gimp.org
# - Matrix: #gnome-maps:gnome.org (unclear if bridged to IRC)
{ pkgs, ... }: { pkgs, ... }:
{ {
sane.programs."gnome.gnome-maps" = { sane.programs."gnome.gnome-maps" = {
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-maps; packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-maps;
suggestedPrograms = [
"geoclue2"
];
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.whitelistDri = true; # for perf sandbox.whitelistDri = true; # for perf
sandbox.whitelistDbus = [ sandbox.whitelistDbus = [

View File

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

View File

@@ -1,7 +1,7 @@
# docs: <https://git.sr.ht/~mil/mepo> # docs: <https://git.sr.ht/~mil/mepo>
# irc #mepo:irc.oftc.net # irc #mepo:irc.oftc.net
#
{ config, lib, ... }: { config, lib, ... }:
{ {
sane.programs.mepo = { sane.programs.mepo = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
@@ -16,11 +16,11 @@
{ type = "file"; path = ".cache/mepo/savestate"; } { type = "file"; path = ".cache/mepo/savestate"; }
]; ];
# give mepo access to gpsd for location data, if that's enabled. # enable geoclue2 and gpsd for location data.
# same with geoclue2. suggestedPrograms = [
suggestedPrograms = lib.optional config.services.gpsd.enable "gpsd" "geoclue2"
++ lib.optional config.services.geoclue2.enable "geoclue2-with-demo-agent" # "gpsd" #< not required, and mepo only uses it if geoclue is unavailable
; ];
}; };
# programs.mepo = lib.mkIf config.sane.programs.mepo.enabled { # programs.mepo = lib.mkIf config.sane.programs.mepo.enabled {

View File

@@ -1,3 +1,19 @@
# 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, ... }: { pkgs, ... }:
{ {
sane.programs.mmcli = { sane.programs.mmcli = {

View File

@@ -0,0 +1,44 @@
[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)

View File

@@ -0,0 +1,21 @@
# 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;
};
}

View File

@@ -2,6 +2,9 @@
{ {
sane.programs.nmcli = { sane.programs.nmcli = {
packageUnwrapped = pkgs.networkmanager-split.nmcli; packageUnwrapped = pkgs.networkmanager-split.nmcli;
# TODO: sandbox sandbox.method = "bwrap";
sandbox.whitelistDbus = [
"system"
];
}; };
} }

View File

@@ -0,0 +1,7 @@
{
"restart-on-display": true,
"restart-delay": 500,
"run-through-compositor": false,
"processes-background-only": true,
"processes-own-only": true
}

View File

@@ -4,11 +4,14 @@
# - add network/bluetooth indicator # - add network/bluetooth indicator
# - <https://github.com/nwg-piotr/nwg-panel/issues/269> # - <https://github.com/nwg-piotr/nwg-panel/issues/269>
# - add CPU/meminfo executor # - add CPU/meminfo executor
# - use sane-sysinfo # - use sane-sysload
{ {
components, controlsSettingsComponents,
height, height,
locker,
modulesRight,
playerctlChars, playerctlChars,
mediaPrevNext,
windowIcon, windowIcon,
windowTitle, windowTitle,
workspaceHideEmpty, workspaceHideEmpty,
@@ -49,9 +52,7 @@
modules-center = [ modules-center = [
"clock" "clock"
]; ];
modules-right = [ modules-right = modulesRight;
"playerctl"
];
clock = { clock = {
angle = 0.0; angle = 0.0;
@@ -79,7 +80,7 @@
battery-low-interval = 4; #< notify every N minutes when battery continues to remain low 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 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 # commands.battery = ""; #< optional action to perform when battery icon is clicked in the drop-down menu
components = components; components = controlsSettingsComponents;
click-closes = false; click-closes = false;
custom-items = []; custom-items = [];
css-name = "controls-window"; css-name = "controls-window";
@@ -90,21 +91,20 @@
menu.icon = "system-shutdown-symbolic"; menu.icon = "system-shutdown-symbolic";
menu.items = [ menu.items = [
{ {
# TODO: plumb through the configured locker instead of assuming `swaylock`
name = "Lock"; name = "Lock";
cmd = "swaylock -f -c 000000"; cmd = "s6-rc start ${locker}";
}
{
name = "Logout";
cmd = "swaymsg exit";
} }
# {
# name = "Logout";
# cmd = "swaymsg exit";
# }
{ {
name = "Reboot"; name = "Reboot";
cmd = "systemctl reboot"; cmd = "reboot";
} }
{ {
name = "Shutdown"; name = "Shutdown";
cmd = "systemctl -i poweroff"; cmd = "shutdown now";
} }
]; ];
menu.name = "Exit"; menu.name = "Exit";
@@ -127,6 +127,23 @@
interval = 2; interval = 2;
label-css-name = "playerctl-label"; label-css-name = "playerctl-label";
scroll = false; 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 = { sway-workspaces = {
angle = 0.0; angle = 0.0;
@@ -143,6 +160,20 @@
show-name = windowTitle; 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: # unused modules:
brightness-slider = {}; brightness-slider = {};
dwl-tags = {}; dwl-tags = {};

View File

@@ -23,7 +23,8 @@ in
# what looks good: # what looks good:
# - 15px on moby # - 15px on moby
# - 24px on lappy # - 24px on lappy
default = lib.min 24 (cfg.config.fontSize - 1); # there's about 10px padding total around this (above + below)
default = lib.min 24 (cfg.config.height - 11);
}; };
fontSize = mkOption { fontSize = mkOption {
type = types.int; type = types.int;
@@ -36,10 +37,18 @@ in
height of the top bar in px. 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"; battery = mkEnableOption' true "display battery status";
brightness = mkEnableOption' true "display backlight level and slider"; brightness = mkEnableOption' true "display backlight level and slider";
mediaTitle = mkEnableOption' true "display title of current song/media"; mediaTitle = mkEnableOption' true "display title of current song/media";
mediaPrevNext = mkEnableOption' true "display prev/next button in 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"; windowIcon = mkEnableOption' true "display icon of active window";
windowTitle = mkEnableOption' true "display title of active window"; windowTitle = mkEnableOption' true "display title of active window";
workspaceNumbers = mkOption { workspaceNumbers = mkOption {
@@ -66,8 +75,16 @@ in
# XXX(2024/06/13): wlr-randr does not cross compile # XXX(2024/06/13): wlr-randr does not cross compile
wlr-randr = null; #< only used if not on sway/hyprland; or if using dwl wlr-randr = null; #< only used if not on sway/hyprland; or if using dwl
}).overrideAttrs (base: { }).overrideAttrs (base: {
patches = (base.patches or []) ++ lib.optionals (!cfg.config.mediaPrevNext) [ # patches = (base.patches or []) ++ lib.optionals (!cfg.config.mediaPrevNext) [
./playerctl-no-prev-next.diff # ./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. # - disable the drop-down chevron by the controls.
@@ -75,17 +92,22 @@ in
# - disable brightness indicator for same reason. # - disable brightness indicator for same reason.
# - *leave* the volume indicator: one *could* remove it, however on desko that would leave the controls pane empty # - *leave* the volume indicator: one *could* remove it, however on desko that would leave the controls pane empty
# making the dropdown inaccessible # 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 "") + '' 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 \ 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, 4)' \
'# 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 \ 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, 4)' \
'# 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 \ 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, 4)' \
# '# 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 # XXX(2024/06/13) the bluetooth stuff doesn't cross compile, so disable it
@@ -102,11 +124,12 @@ in
src = ./style.css; src = ./style.css;
inherit (cfg.config) fontSize clockFontSize; 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 { fs.".config/nwg-panel/config".symlink.target = pkgs.writers.writeJSON "config" (import ./config.nix {
inherit (cfg.config) height windowIcon windowTitle workspaceHideEmpty workspaceNumbers; inherit (cfg.config) locker height mediaPrevNext windowIcon windowTitle workspaceHideEmpty workspaceNumbers;
# component order matters, mostly for the drop-down. # component order matters, mostly for the drop-down.
# default for most tools (e.g. swaync) is brightness control above volume. # default for most tools (e.g. swaync) is brightness control above volume.
components = controlsSettingsComponents =
lib.optionals cfg.config.brightness [ lib.optionals cfg.config.brightness [
"brightness" "brightness"
] ++ [ ] ++ [
@@ -116,6 +139,11 @@ in
"battery" "battery"
] ]
; ;
modulesRight = [
"playerctl"
] ++ lib.optionals cfg.config.sysload [
"executor-sysload"
];
playerctlChars = if cfg.config.mediaTitle then 60 else 0; playerctlChars = if cfg.config.mediaTitle then 60 else 0;
}); });

View File

@@ -1,8 +1,16 @@
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 diff --git a/nwg_panel/modules/playerctl.py b/nwg_panel/modules/playerctl.py
index 9b53b4b..c4d96ae 100644 index ff48d4c..43ae221 100644
--- a/nwg_panel/modules/playerctl.py --- a/nwg_panel/modules/playerctl.py
+++ b/nwg_panel/modules/playerctl.py +++ b/nwg_panel/modules/playerctl.py
@@ -180,15 +180,6 @@ class Playerctl(Gtk.EventBox): @@ -211,15 +211,6 @@ class Playerctl(Gtk.EventBox):
if self.settings["angle"] != 0.0: if self.settings["angle"] != 0.0:
button_box.set_orientation(Gtk.Orientation.VERTICAL) button_box.set_orientation(Gtk.Orientation.VERTICAL)
@@ -18,7 +26,7 @@ index 9b53b4b..c4d96ae 100644
self.play_pause_btn = Gtk.Button() self.play_pause_btn = Gtk.Button()
if self.settings["button-css-name"]: if self.settings["button-css-name"]:
self.play_pause_btn.set_property("name", self.settings["button-css-name"]) self.play_pause_btn.set_property("name", self.settings["button-css-name"])
@@ -198,15 +189,6 @@ class Playerctl(Gtk.EventBox): @@ -229,15 +220,6 @@ class Playerctl(Gtk.EventBox):
self.play_pause_btn.connect("clicked", self.launch, self.PlayerOps.PLAY_PAUSE) self.play_pause_btn.connect("clicked", self.launch, self.PlayerOps.PLAY_PAUSE)
button_box.pack_start(self.play_pause_btn, False, False, 1) button_box.pack_start(self.play_pause_btn, False, False, 1)
@@ -31,6 +39,21 @@ index 9b53b4b..c4d96ae 100644
- btn.connect("clicked", self.launch, self.PlayerOps.NEXT) - btn.connect("clicked", self.launch, self.PlayerOps.NEXT)
- button_box.pack_start(btn, False, False, 1) - button_box.pack_start(btn, False, False, 1)
- -
self.label = AutoScrollLabel(self.settings["scroll"], self.num_players_lbl = Gtk.Label.new("")
self.settings["chars"], if self.settings["label-css-name"]:
self.settings["interval"]) 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):

View File

@@ -20,35 +20,61 @@
font-size: @fontSize@px; font-size: @fontSize@px;
} }
button {
margin: 2px;
}
#task-box {
padding-left: 4px;
padding-right: 4px;
}
#task-box-focused { #task-box-focused {
background-color: @accent-g2; background-color: @accent-g2;
padding-left: 4px;
padding-right: 4px;
} }
#playerctl-button { #playerctl-button {
background-color: rgba(0, 0, 0, 0.08); background-color: rgba(0, 0, 0, 0.08);
background-image: none; background-image: none;
border: none; border: none;
box-shadow: none; box-shadow: none;
margin: -1; /* 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; 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 { #panel-top {
background: @accent-g1; background: @accent-g1;
color: @fg1; 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 */ /* increase the size of each workspace icon */
#sway-workspaces-item > label { #sway-workspaces-item > label {

View File

@@ -0,0 +1,45 @@
# 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.python3Packages.ols;
fs.".config/ols/cell.db".symlink.target = pkgs.runCommandLocal "cell.db" {
nativeBuildInputs = [ pkgs.python3Packages.ols ];
} ''
cellid-ols-import -o "$out" "${pkgs.opencellid}"
'';
persist.byStore.private = [
".local/share/ols"
];
secrets.".config/ols/ols.toml" = ../../../secrets/common/ols.toml.bin;
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" ];
};
};
}

View File

@@ -0,0 +1,8 @@
{ pkgs, ... }:
{
sane.programs.pidof = {
packageUnwrapped = pkgs.linkIntoOwnPackage pkgs.procps "bin/pidof";
sandbox.method = "bwrap";
sandbox.isolatePids = false;
};
}

View File

@@ -116,6 +116,7 @@ in
fs.".config/rofi/config.rasi".symlink.target = ./config.rasi; fs.".config/rofi/config.rasi".symlink.target = ./config.rasi;
fs."Apps".symlink.target = ".local/share/applications/rofi-applications.desktop"; fs."Apps".symlink.target = ".local/share/applications/rofi-applications.desktop";
fs."WiFi".symlink.target = ".local/share/applications/networkmanager_dmenu.desktop";
persist.byStore.cryptClearOnBoot = [ persist.byStore.cryptClearOnBoot = [
# this gets us a few things: # this gets us a few things:
# - file browser remembers its last directory # - file browser remembers its last directory

View File

@@ -1,6 +1,6 @@
{ ... }: { ... }:
{ {
sane.programs.sane-sysinfo = { sane.programs.sane-sysload = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.extraPaths = [ sandbox.extraPaths = [
"/sys/class/power_supply" "/sys/class/power_supply"

View File

@@ -44,19 +44,19 @@ in
# XXX: /run/current-system symlink can't be cached without forcing regular mass rebuilds: # XXX: /run/current-system symlink can't be cached without forcing regular mass rebuilds:
# mount it as if it were a directory instead. # mount it as if it were a directory instead.
"/run/current-system" = ""; "/run/current-system" = "";
} // lib.optionalAttrs config.hardware.opengl.enable { } // lib.optionalAttrs config.hardware.graphics.enable {
"/run/opengl-driver" = let "/run/opengl-driver" = let
gl = config.hardware.opengl; gl = config.hardware.graphics;
# from: <repo:nixos/nixpkgs:nixos/modules/hardware/opengl.nix> # from: <repo:nixos/nixpkgs:nixos/modules/hardware/graphics.nix>
package = pkgs.buildEnv { package = pkgs.buildEnv {
name = "opengl-drivers"; name = "opengl-drivers";
paths = [ gl.package ] ++ gl.extraPackages; paths = [ gl.package ] ++ gl.extraPackages;
}; };
in "${package}"; in "${package}";
} // lib.optionalAttrs (config.hardware.opengl.enable && config.hardware.opengl.driSupport32Bit) { } // lib.optionalAttrs (config.hardware.graphics.enable && config.hardware.graphics.enable32Bit) {
"/run/opengl-driver-32" = let "/run/opengl-driver-32" = let
gl = config.hardware.opengl; gl = config.hardware.graphics;
# from: <repo:nixos/nixpkgs:nixos/modules/hardware/opengl.nix> # from: <repo:nixos/nixpkgs:nixos/modules/hardware/graphics.nix>
package = pkgs.buildEnv { package = pkgs.buildEnv {
name = "opengl-drivers-32bit"; name = "opengl-drivers-32bit";
paths = [ gl.package32 ] ++ gl.extraPackages32; paths = [ gl.package32 ] ++ gl.extraPackages32;

View File

@@ -0,0 +1,53 @@
# 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 = {};
}

View File

@@ -148,6 +148,7 @@ in
"fontconfig" "fontconfig"
# "gnome.gnome-bluetooth" # XXX(2023/05/14): broken # "gnome.gnome-bluetooth" # XXX(2023/05/14): broken
# "gnome.gnome-control-center" # XXX(2023/06/28): depends on webkitgtk4_1 # "gnome.gnome-control-center" # XXX(2023/06/28): depends on webkitgtk4_1
"networkmanager_dmenu"
"nwg-panel" "nwg-panel"
"pipewire" "pipewire"
"playerctl" # for waybar & particularly to have playerctld running "playerctl" # for waybar & particularly to have playerctld running
@@ -163,7 +164,7 @@ in
"swayidle" # enable if you need it "swayidle" # enable if you need it
"swaynotificationcenter" # notification daemon "swaynotificationcenter" # notification daemon
"switchboard" # network/bluetooth/sound control panel "switchboard" # network/bluetooth/sound control panel
"sysvol" # volume notifier "syshud" # volume notifier
"unl0kr" # greeter "unl0kr" # greeter
# "waybar" # "waybar"
"wdisplays" # like xrandr "wdisplays" # like xrandr
@@ -198,6 +199,7 @@ in
sandbox.whitelistAudio = true; # it runs playerctl directly sandbox.whitelistAudio = true; # it runs playerctl directly
sandbox.whitelistDbus = [ "system" "user" ]; # to e.g. launch apps sandbox.whitelistDbus = [ "system" "user" ]; # to e.g. launch apps
sandbox.whitelistDri = true; sandbox.whitelistDri = true;
sandbox.whitelistS6 = true; #< for Super+L to start the screen locker service
sandbox.whitelistX = true; # sway invokes xwayland itself sandbox.whitelistX = true; # sway invokes xwayland itself
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.extraRuntimePaths = [ sandbox.extraRuntimePaths = [
@@ -227,7 +229,7 @@ in
''; '';
fs.".config/sway/config".symlink.target = pkgs.substituteAll { fs.".config/sway/config".symlink.target = pkgs.substituteAll {
src = ./sway-config; src = ./config;
inherit (cfg.config) inherit (cfg.config)
extra_lines extra_lines
font font

View File

@@ -32,6 +32,7 @@ in
}; };
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.whitelistS6 = true; sandbox.whitelistS6 = true;
sandbox.isolatePids = false; #< XXX: not sure why, but swaync segfaults under load without this!
}; };
sane.programs.swaync-fbcli = { sane.programs.swaync-fbcli = {
@@ -61,6 +62,8 @@ in
name of entry in /sys/class/backlight which indicates the primary backlight. 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 = {}; default = {};
@@ -116,27 +119,14 @@ in
env.GNOTIFICATION_BACKEND = "freedesktop"; env.GNOTIFICATION_BACKEND = "freedesktop";
fs.".config/swaync/style.css".symlink.target = ./style.css; fs.".config/swaync/style.css".symlink.target = ./style.css;
fs.".config/swaync/config.json".symlink.text = builtins.toJSON { fs.".config/swaync/config.json".symlink.target = pkgs.writers.writeJSON "config.json" {
"$schema" = "/etc/xdg/swaync/configSchema.json"; "$schema" = "/etc/xdg/swaync/configSchema.json";
positionX = "right"; control-center-height = 600;
positionY = "top";
layer = "overlay";
control-center-layer = "top"; control-center-layer = "top";
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-bottom = 0;
control-center-margin-right = 0;
control-center-margin-left = 0; control-center-margin-left = 0;
notification-2fa-action = true; control-center-margin-right = 0;
notification-inline-replies = false; control-center-margin-top = 0;
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: # control-center-width:
# pinephone native display is 720 x 1440 # pinephone native display is 720 x 1440
# - for compositor scale=2.0 => 360 # - for compositor scale=2.0 => 360
@@ -144,14 +134,28 @@ in
# - for compositor scale=1.6 => 450 # - for compositor scale=1.6 => 450
# if it's set to something wider than the screen, then it overflows and items aren't visible. # if it's set to something wider than the screen, then it overflows and items aren't visible.
control-center-width = 360; control-center-width = 360;
control-center-height = 600; cssPriority = "user"; # "application"|"user". "user" in order to override the system gtk theme.
notification-window-width = 360; fit-to-screen = true; #< have notification center take full vertical screen space
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; 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;
notification-window-width = 360;
positionX = "right";
positionY = "top";
script-fail-notify = true; script-fail-notify = true;
timeout = 30;
timeout-critical = 0;
timeout-low = 5;
transition-time = 100;
inherit scripts; inherit scripts;
widgets = [ widgets = [
# what to show in the notification center (and in which order). # what to show in the notification center (and in which order).
@@ -164,9 +168,13 @@ in
"dnd" "dnd"
"inhibitors" "inhibitors"
"buttons-grid" "buttons-grid"
] ++ lib.optionals cfg.config.enableBacklight [
"backlight" "backlight"
] ++ [
"volume" "volume"
] ++ lib.optionals cfg.config.enableMpris [
"mpris" "mpris"
] ++ [
"notifications" "notifications"
]; ];
widget-config = { widget-config = {
@@ -228,7 +236,10 @@ in
depends = [ "sound" ]; #< TODO: else it will NEVER see the pulse socket in its sandbox depends = [ "sound" ]; #< TODO: else it will NEVER see the pulse socket in its sandbox
partOf = [ "graphical-session" ]; partOf = [ "graphical-session" ];
command = "env G_MESSAGES_DEBUG=all swaync"; # 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";
readiness.waitDbus = "org.freedesktop.Notifications"; readiness.waitDbus = "org.freedesktop.Notifications";
}; };
}; };

View File

@@ -24,7 +24,7 @@
# - SWAYNC_SUMMARY # - SWAYNC_SUMMARY
# rules to use for testing. trigger with: # rules to use for testing. trigger with:
# - `notify-send --app-id=foo subject body` (etc) # - `notify-send --app-name=foo subject body` (etc)
# should also be possible to trigger via any messaging app # should also be possible to trigger via any messaging app
fbcli-test-im = { fbcli-test-im = {
body = "test:message"; body = "test:message";

View File

@@ -17,7 +17,12 @@ log() {
checkActive() { checkActive() {
# simulate a dry-run start. if no actions would be performed, then the service is up. # simulate a dry-run start. if no actions would be performed, then the service is up.
# alternative is s6-svstat, but that doesn't support oneshots # alternative is s6-svstat, but that doesn't support oneshots
test -z "$(s6-rc -n 0 -b start "$service")" && echo true || echo false local s6Output=$(s6-rc -n 0 -b start "$service")
if [ -z "$s6Output" ]; then
echo true
else
echo false
fi
} }
startService() { startService() {
log "startService: $service" log "startService: $service"

View File

@@ -1,3 +1,6 @@
# BUGS
# - switchboard-plug-sound errors because
# GLib-GIO-ERROR **: Settings schema 'org.gnome.settings-daemon.plugins.media-keys' is not installed
{ pkgs, ... }: { pkgs, ... }:
{ {
sane.programs.switchboard = { sane.programs.switchboard = {
@@ -24,5 +27,9 @@
]; ];
xorg = pkgs.buildPackages.xorg; #< cross compilation fix (TODO: upstream) xorg = pkgs.buildPackages.xorg; #< cross compilation fix (TODO: upstream)
}; };
sandbox.method = "bwrap";
sandbox.whitelistWayland = true;
sandbox.whitelistDbus = [ "system" ]; #< to speak with NetworkManager
sandbox.whitelistAudio = true; #< even with this, the sound plugin doesn't seem to work...
}; };
} }

View File

@@ -1,11 +1,15 @@
{ ... }: { ... }:
{ {
sane.programs.sysvol = { sane.programs.syshud = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.whitelistAudio = true; sandbox.whitelistAudio = true;
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.extraPaths = [
"/sys/class/backlight" #< crashes if unable to access this directory
# "/sys/devices" #< only if you want it to actually show when the backlight changes
];
fs.".config/sys64/volume.css".symlink.text = '' fs.".config/sys64/hud.css".symlink.text = ''
window { window {
background: transparent; background: transparent;
} }
@@ -53,8 +57,8 @@
} }
''; '';
services."sysvol" = { services."syshud" = {
description = "sysvol: volume monitor/notifier"; description = "syshud: volume monitor/notifier";
depends = [ "sound" ]; #< specifically wireplumber-pulse depends = [ "sound" ]; #< specifically wireplumber-pulse
partOf = [ "graphical-session" ]; partOf = [ "graphical-session" ];
@@ -67,7 +71,7 @@
# -{H,W} N to set the height/width of the notifier, in px. # -{H,W} N to set the height/width of the notifier, in px.
# -i N to set the size of the volume icon # -i N to set the size of the volume icon
# -P to hide percentage text # -P to hide percentage text
command = "sysvol -p top -t 1 -T 0 -m 22 -H 39 -W 256 -i 32 -P"; command = "syshud -p top -t 1 -T 0 -m 22 -H 39 -W 256 -i 32 -P";
}; };
}; };
} }

View File

@@ -0,0 +1,11 @@
{ config, pkgs, ... }:
{
sane.programs.where-am-i = {
# packageUnwrapped = pkgs.linkIntoOwnPackage config.sane.programs.geoclue2.packageUnwrapped "libexec/geoclue-2.0/demos/where-am-i";
packageUnwrapped = pkgs.linkFarm "where-am-i" [{
# bring the `where-am-i` tool into a `bin/` directory so it can be invokable via PATH
name = "bin/where-am-i";
path = "${config.sane.programs.geoclue2.packageUnwrapped}/libexec/geoclue-2.0/demos/where-am-i";
}];
};
}

View File

@@ -1,13 +1,17 @@
{ config, lib, sane-lib, ... }: { config, lib, sane-lib, ... }:
let let
keysForHost = hostName: let hostKeys = lib.mapAttrsToList
hostCfg = config.sane.hosts.by-name."${hostName}"; (hostName: hostCfg:
in { # generate `root@servo`, `colin@servo`, `root@servo-hn`, `colin@servo-hn`, ... as a single attrset:
"root@${hostName}" = hostCfg.ssh.host_pubkey; lib.foldl' (acc: alias: acc // {
"colin@${hostName}" = lib.mkIf (hostCfg.ssh.user_pubkey != null && hostCfg.ssh.authorized) hostCfg.ssh.user_pubkey; "root@${alias}" = hostCfg.ssh.host_pubkey;
}; "colin@${alias}" = lib.mkIf (hostCfg.ssh.user_pubkey != null && hostCfg.ssh.authorized) hostCfg.ssh.user_pubkey;
hostKeys = builtins.map keysForHost (builtins.attrNames config.sane.hosts.by-name); })
{}
hostCfg.names
)
config.sane.hosts.by-name;
in in
{ {
sane.ssh.pubkeys = lib.mkMerge (hostKeys ++ [ sane.ssh.pubkeys = lib.mkMerge (hostKeys ++ [

View File

@@ -1,10 +1,10 @@
# trampoline from flake.nix into the specific host definition, while doing a tiny bit of common setup # trampoline from flake.nix into the specific host definition, while doing a tiny bit of common setup
# args from flake-level `import` # args from flake-level `import`
{ hostName }: { hostName, variant }:
# module args # module args
{ ... }: { lib, ... }:
{ {
imports = [ imports = [
@@ -14,4 +14,14 @@
]; ];
networking.hostName = hostName; networking.hostName = hostName;
system.name = if variant == null then
hostName
else
"${hostName}-${variant}"
;
sane = lib.mkMerge [
(lib.mkIf (variant == "min") { maxBuildCost = 0; })
(lib.mkIf (variant == "light") { maxBuildCost = 2; })
];
} }

View File

@@ -45,6 +45,9 @@
actions.screenoff.delay = 300; actions.screenoff.delay = 300;
actions.screenoff.enable = true; actions.screenoff.enable = true;
}; };
sane.programs.swaynotificationcenter.config = {
enableMpris = false; #< consumes too much screen real-estate
};
sane.programs.waybar.config = { sane.programs.waybar.config = {
fontSize = 14; fontSize = 14;
@@ -63,6 +66,7 @@
windowTitle = false; windowTitle = false;
mediaPrevNext = false; mediaPrevNext = false;
mediaTitle = false; mediaTitle = false;
sysload = false;
workspaceNumbers = [ "1" "2" "3" "4" "5" ]; workspaceNumbers = [ "1" "2" "3" "4" "5" ];
workspaceHideEmpty = false; workspaceHideEmpty = false;
}; };

View File

@@ -80,6 +80,7 @@ in
protocol = [ "udp" ]; protocol = [ "udp" ];
visibleTo.lan = true; visibleTo.lan = true;
visibleTo.wan = cfg.visibleToWan; visibleTo.wan = cfg.visibleToWan;
visibleTo.doof = cfg.visibleToWan;
description = "colin-wireguard"; description = "colin-wireguard";
}; };

62
impure.nix Normal file
View File

@@ -0,0 +1,62 @@
# this entry-point exposes all packages, hosts, etc, but with no purity guarnatees.
# the intended way to use this is to first copy every .nix file and dependency in this repo to the nix store, then enter this file.
# entering this file *before* copying anything into the nix store can cause interesting
# race conditions or eval failures.
#
# see default.nix for a wrapper around this with better purity guarantees.
{ }:
let
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 (
[
(import ./hosts/instantiate.nix { hostName = name; inherit variant; })
(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 {}) // {
config = host.config;
fs = host.config.sane.fs;
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: {
# TODO: swap order: $host-{next,staging}-{min,light}:
# then lexicographically-adjacent targets would also have the minimal difference in closure,
# and the order in which each target should be built is more evident
"${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 {
inherit hosts;
} // (mkPkgs {})

View File

@@ -0,0 +1,5 @@
# this is the entry point for `nix-update`, used when i update the packages in this repo.
# nix-update needs to work on the actual out-of-store source,
# which means it can't call through the hermetic `default.nix` at the top of this repo,
# but rather needs the in-place `impure.nix` entry point.
import ../../impure.nix

View File

@@ -26,9 +26,10 @@ let
}; };
}; };
mkNetNsConfig = name: opts: with opts; { mkNetNsConfig = name: opts: with opts; {
networking.localCommands = let systemd.services."netns-${name}" = let
iptables = "${pkgs.iptables}/bin/iptables"; ip = lib.getExe' pkgs.iproute2 "ip";
in-ns = "ip netns exec ${name}"; iptables = lib.getExe pkgs.iptables;
in-ns = "${ip} netns exec ${name}";
bridgePort = port: proto: '' bridgePort = port: proto: ''
${in-ns} ${iptables} -A PREROUTING -t nat -p ${proto} --dport ${port} -m iprange --dst-range ${netnsPubIpv4} \ ${in-ns} ${iptables} -A PREROUTING -t nat -p ${proto} --dport ${port} -m iprange --dst-range ${netnsPubIpv4} \
-j DNAT --to-destination ${hostVethIpv4} -j DNAT --to-destination ${hostVethIpv4}
@@ -41,55 +42,61 @@ let
config.sane.ports.ports config.sane.ports.ports
) )
; ;
in '' in {
ip netns add ${name} || (test -e /run/netns/${name} && echo "${name} already exists") description = "create a network namespace which will selectively bridge traffic with the init namespace";
# specifically, we need to set these up before wireguard-wg-*,
wantedBy = [ "network-pre.target" ];
before = [ "network-pre.target" ];
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
script = ''
${ip} netns add ${name} || (test -e /run/netns/${name} && echo "${name} already exists")
# DOCS: # DOCS:
# - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/> # - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/>
# - iptables primer: <https://danielmiessler.com/study/iptables/> # - iptables primer: <https://danielmiessler.com/study/iptables/>
# create veth pair # create veth pair
ip link add ${name}-veth-a type veth peer name ${name}-veth-b || echo "${name}-veth-{a,b} aleady exists" ${ip} link add ${name}-veth-a type veth peer name ${name}-veth-b || echo "${name}-veth-{a,b} aleady exists"
ip addr add ${hostVethIpv4}/24 dev ${name}-veth-a || echo "${name}-veth-a aleady has IP address" ${ip} addr add ${hostVethIpv4}/24 dev ${name}-veth-a || echo "${name}-veth-a aleady has IP address"
ip link set ${name}-veth-a up ${ip} link set ${name}-veth-a up
# move veth-b into the namespace # move veth-b into the namespace
ip link set ${name}-veth-b netns ${name} || echo "${name}-veth-b was already moved into its netns" ${ip} link set ${name}-veth-b netns ${name} || echo "${name}-veth-b was already moved into its netns"
${in-ns} ip addr add ${netnsVethIpv4}/24 dev ${name}-veth-b || echo "${name}-veth-b aleady has IP address" ${in-ns} ${ip} addr add ${netnsVethIpv4}/24 dev ${name}-veth-b || echo "${name}-veth-b aleady has IP address"
${in-ns} ip link set ${name}-veth-b up ${in-ns} ${ip} link set ${name}-veth-b up
# make it so traffic originating from the host side of the veth # make it so traffic originating from the host side of the veth
# is sent over the veth no matter its destination. # is sent over the veth no matter its destination.
ip rule add from ${hostVethIpv4} lookup ${name} pref 50 || echo "${name} already has ip rules (pref 50)" ${ip} rule add from ${hostVethIpv4} lookup ${name} pref 50 || echo "${name} already has ip rules (pref 50)"
# for traffic originating at the host veth to the WAN, use the veth as our gateway # for traffic originating at the host veth to the WAN, use the veth as our gateway
# not sure if the metric 1002 matters. # not sure if the metric 1002 matters.
ip route add default via ${netnsVethIpv4} dev ${name}-veth-a proto kernel src ${hostVethIpv4} metric 1002 table ${name} || \ ${ip} route add default via ${netnsVethIpv4} dev ${name}-veth-a proto kernel src ${hostVethIpv4} metric 1002 table ${name} || \
echo "${name} already has default route" echo "${name} already has default route"
# give the default route lower priority # give the default route lower priority
ip rule add from all lookup local pref 100 || echo "${name}: already has ip rules (pref 100)" ${ip} rule add from all lookup local pref 100 || echo "${name}: already has ip rules (pref 100)"
ip rule del from all lookup local pref 0 || echo "${name}: already removed ip rule of default lookup (pref 0)" ${ip} rule del from all lookup local pref 0 || echo "${name}: already removed ip rule of default lookup (pref 0)"
# in order to access DNS in this netns, we need to route it to the VPN's nameservers # in order to access DNS in this netns, we need to route it to the VPN's nameservers
# - alternatively, we could fix DNS servers like 1.1.1.1. # - alternatively, we could fix DNS servers like 1.1.1.1.
${in-ns} ${iptables} -A OUTPUT -t nat -p udp --dport 53 -m iprange --dst-range 127.0.0.53 \ ${in-ns} ${iptables} -A OUTPUT -t nat -p udp --dport 53 -m iprange --dst-range 127.0.0.53 \
-j DNAT --to-destination ${dns}:53 -j DNAT --to-destination ${dns}:53
'' + (lib.concatStringsSep "\n" bridgeStatements); '' + (lib.concatStringsSep "\n" bridgeStatements);
preStop = ''
${in-ns} ${ip} link del ${name}-veth-b || echo "couldn't delete ${name}-veth-b"
${ip} link del ${name}-veth-a || echo "couldn't delete ${name}-veth-a"
${ip} netns delete ${name} || echo "couldn't delete ${name}"
# restore rules/routes
${ip} rule del from ${hostVethIpv4} lookup ${name} pref 50 || echo "couldn't delete init -> ${name} rule"
${ip} route del default via ${netnsVethIpv4} dev ${name}-veth-a proto kernel src ${hostVethIpv4} metric 1002 table ${name} || echo "couldn't delete init > ${name} route"
# FIXME: if there are other net namespaces active, changing the prefs here may break those!
${ip} rule add from all lookup local pref 0
${ip} rule del from all lookup local pref 100
'';
};
# postShutdown = '' # for some reason network-pre doesn't actually get run before network.target by default??
# ${in-ns} ip link del ${name}-veth-b || echo "couldn't delete ${name}-veth-b" systemd.targets.network-pre.wantedBy = [ "network.target" ];
# ip link del ${name}-veth-a || echo "couldn't delete ${name}-veth-a" systemd.targets.network-pre.before = [ "network.target" ];
# ip netns delete ${name} || echo "couldn't delete ${name}"
# # restore rules/routes
# ip rule del from ${veth-host-ip} lookup ${name} pref 50 || echo "couldn't delete init -> ${name} rule"
# ip route del default via ${veth-local-ip} dev ${name}-veth-a proto kernel src ${veth-host-ip} metric 1002 table ${name} || echo "couldn't delete init > #{name} route"
# ip rule add from all lookup local pref 0
# ip rule del from all lookup local pref 100
# '';
# specifically, we need to set these up before wireguard-wg-*,
# whose unit files are BEFORE "network.target", and therefore
# ordered ambiguously w.r.t. network-local-commands (a dep of "network.target").
systemd.services.network-local-commands.wantedBy = [ "network-pre.target" ];
systemd.services.network-local-commands.before = [ "network-pre.target" ];
# create a new routing table that we can use to proxy traffic out of the root namespace # create a new routing table that we can use to proxy traffic out of the root namespace
# through the wireguard namespaces, and to the WAN via VPN. # through the wireguard namespaces, and to the WAN via VPN.
@@ -114,7 +121,8 @@ in
networking.localCommands = f.networking.localCommands; networking.localCommands = f.networking.localCommands;
networking.iproute2.rttablesExtraConfig = f.networking.iproute2.rttablesExtraConfig; networking.iproute2.rttablesExtraConfig = f.networking.iproute2.rttablesExtraConfig;
networking.iproute2.enable = f.networking.iproute2.enable; networking.iproute2.enable = f.networking.iproute2.enable;
systemd.services.network-local-commands = f.systemd.services.network-local-commands; systemd.services = f.systemd.services;
systemd.targets.network-pre = f.systemd.targets.network-pre;
}; };
in take (sane-lib.mkTypedMerge take configs); in take (sane-lib.mkTypedMerge take configs);
} }

View File

@@ -96,7 +96,13 @@ let
} // extraConfig } // extraConfig
); );
configPath = "/var/lib/trust-dns/${flavor}-config.toml"; configPath = "/var/lib/trust-dns/${flavor}-config.toml";
sedArgs = lib.mapAttrsToList (key: value: ''-e "s/${key}/${value}/g"'') substitutions; sedArgs = builtins.map (key: ''-e "s/${key}/${substitutions."${key}"}/g"'') (
# HACK: %ANATIVE% often expands to one of the other subtitutions (e.g. %AWAN%)
# so we must expand it *first*.
lib.sortOn
(k: if k == "%ANATIVE%" then 0 else 1)
(builtins.attrNames substitutions)
);
subs = lib.concatStringsSep " " sedArgs; subs = lib.concatStringsSep " " sedArgs;
in { in {
description = "trust-dns Domain Name Server (serving ${flavor})"; description = "trust-dns Domain Name Server (serving ${flavor})";

View File

@@ -127,14 +127,12 @@ let
FirewallMark = fwmark; FirewallMark = fwmark;
}; };
wireguardPeers = [{ wireguardPeers = [{
wireguardPeerConfig = {
AllowedIPs = [ AllowedIPs = [
"0.0.0.0/0" "0.0.0.0/0"
"::/0" "::/0"
]; ];
Endpoint = endpoint; Endpoint = endpoint;
PublicKey = publicKey; PublicKey = publicKey;
};
}]; }];
}; };
@@ -149,10 +147,10 @@ let
# Domains = ~.: system DNS queries are sent to this link's DNS server # Domains = ~.: system DNS queries are sent to this link's DNS server
# networkConfig.Domains = "~."; # networkConfig.Domains = "~.";
routes = [{ routes = [{
routeConfig.Table = id; Table = id;
routeConfig.Scope = "link"; Scope = "link";
routeConfig.Destination = "0.0.0.0/0"; Destination = "0.0.0.0/0";
routeConfig.Source = addrV4; Source = addrV4;
}]; }];
# RequiredForOnline => should `systemd-networkd-wait-online` fail if this network can't come up? # RequiredForOnline => should `systemd-networkd-wait-online` fail if this network can't come up?
linkConfig.RequiredForOnline = false; linkConfig.RequiredForOnline = false;

View File

@@ -2,17 +2,18 @@
, fetchFromGitHub , fetchFromGitHub
, lib , lib
, makeWrapper , makeWrapper
, nix-update-script
, pulseaudio , pulseaudio
}: }:
buildGoModule rec { buildGoModule rec {
pname = "blast-ugjka"; pname = "blast-ugjka";
version = "0.6.2"; version = "0.7.0";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "ugjka"; owner = "ugjka";
repo = "blast"; repo = "blast";
rev = "v${version}"; rev = "v${version}";
hash = "sha256-Y9Jj+UrrsyRfihHAdC354jb1385xqLIufB0DoikrXYM="; hash = "sha256-yMwMG0y2ehq2dBMlv9hF+i0TgmMjW3ojBVGiqEUSrhU=";
}; };
vendorHash = "sha256-yPwLilMiDR1aSeuk8AEmuYPsHPRWqiByGLwgkdI5t+s="; vendorHash = "sha256-yPwLilMiDR1aSeuk8AEmuYPsHPRWqiByGLwgkdI5t+s=";
@@ -26,10 +27,12 @@ buildGoModule rec {
--suffix PATH : ${lib.makeBinPath [ pulseaudio ]} --suffix PATH : ${lib.makeBinPath [ pulseaudio ]}
''; '';
passthru.updateScript = nix-update-script { };
meta = with lib; { meta = with lib; {
description = "blast your linux audio to DLNA receivers"; description = "blast your linux audio to DLNA receivers";
# license = licenses.mit; # MIT + NoAI # license = licenses.mit; # MIT + NoAI
inherit (src.meta) homepage; homepage = "https://github.com/ugjka/blast";
maintainers = with maintainers; [ colinsane ]; maintainers = with maintainers; [ colinsane ];
platforms = platforms.unix; platforms = platforms.unix;
}; };

View File

@@ -8,6 +8,15 @@
# #
# this script downloads assisted GPS (AGPS) data via the system's default gateway (i.e. WiFi) # this script downloads assisted GPS (AGPS) data via the system's default gateway (i.e. WiFi)
# and shares that with the modem. this quickens the process of acquiring a GPS fix. # and shares that with the modem. this quickens the process of acquiring a GPS fix.
# AGPS data includes an almanac and "ephemeris" [Wikipedia-GPS_signals#Navigation_message]
# - Almanac: valid for 2 weeks; status & low-res orbital info for *all* satellites.
# - used to know which GPS signals to *search* for.
# - every sat broadcasts the global almanac over 12.5min cycles.
# - Ephemeris: valid for 4 hours; precise orbital info for each satellite.
# - takes 30s to learn this info w/o AGPS data.
# - every sat broadcasts *its own* ephemeris over 30s cycles.
#
# [Wikipedia-GPS_signals#Navigation_message]: https://en.wikipedia.org/wiki/GPS_signals#Navigation_message
# #
# the script may also configure other parts of the modem as `eg25-manager` does. # the script may also configure other parts of the modem as `eg25-manager` does.
# these options are less tested: see `--help` for more. # these options are less tested: see `--help` for more.

View File

@@ -145,8 +145,8 @@ in (lib.makeScope newScope (self: with self; {
extid = "webextension@metamask.io"; extid = "webextension@metamask.io";
pname = "ether-metamask"; pname = "ether-metamask";
url = "https://github.com/MetaMask/metamask-extension/releases/download/v${version}/metamask-firefox-${version}.zip"; url = "https://github.com/MetaMask/metamask-extension/releases/download/v${version}/metamask-firefox-${version}.zip";
version = "11.16.9"; version = "11.16.13";
hash = "sha256-IYRCR0jX2agMHj/Pnnc+q6X92yrhdIJF+uRtw7C2T2k="; hash = "sha256-aiWFfGGbjFfKEJSe2wnCfxSABs6ELQ2h0x90IAF3wec=";
}; };
fx_cast = fetchVersionedAddon rec { fx_cast = fetchVersionedAddon rec {
extid = "fx_cast@matt.tf"; extid = "fx_cast@matt.tf";
@@ -173,8 +173,8 @@ in (lib.makeScope newScope (self: with self; {
extid = "@ublacklist"; extid = "@ublacklist";
pname = "ublacklist"; pname = "ublacklist";
url = "https://github.com/iorate/ublacklist/releases/download/v${version}/ublacklist-v${version}-firefox.zip"; url = "https://github.com/iorate/ublacklist/releases/download/v${version}/ublacklist-v${version}-firefox.zip";
version = "8.7.1"; version = "8.8.1";
hash = "sha256-FvZ2IFlvoAYMmZFXTkGtCZ+44MmXioA271DXvNY96j8="; hash = "sha256-b9XiA62Hc32enn04DfUJDVW+Wab5zKgUFwO+oFeTT/w=";
}; };
ublock-origin = fetchVersionedAddon rec { ublock-origin = fetchVersionedAddon rec {
extid = "uBlock0@raymondhill.net"; extid = "uBlock0@raymondhill.net";

View File

@@ -1,13 +1,14 @@
{ buildLinux { lib
, buildLinux
, fetchFromGitLab , fetchFromGitLab
#v nixpkgs calls `.override` on the kernel to configure additional things #v nixpkgs calls `.override` on the kernel to configure additional things
, features ? {} , features ? {}
, kernelPatches ? [] , kernelPatches ? null
, randstructSeed ? "" , randstructSeed ? ""
, structuredExtraConfig ? {} , structuredExtraConfig ? {}
, ... , ...
}: }:
buildLinux { buildLinux ({
src = fetchFromGitLab { src = fetchFromGitLab {
owner = "exynos5-mainline"; owner = "exynos5-mainline";
repo = "linux"; repo = "linux";
@@ -23,5 +24,9 @@ buildLinux {
autoModules = false; autoModules = false;
# preferBuiltin = false; # preferBuiltin = false;
inherit features kernelPatches randstructSeed structuredExtraConfig; inherit features randstructSeed structuredExtraConfig;
} } // lib.optionalAttrs (builtins.isList kernelPatches) {
# callPackage mucks with `kernelPatches`: only forward this argument if it's a list,
# as expected by `buildLinux`
inherit kernelPatches;
})

View File

@@ -7,8 +7,8 @@ let
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "nix-community"; owner = "nix-community";
repo = "nixpkgs-wayland"; repo = "nixpkgs-wayland";
rev = "422bb5c5106008418f499ca591c0138f73e213f4"; rev = "d0eb8a2eb4b9e30e10b95ed5d99f3501c1b3367d";
hash = "sha256-32ukRcPI2W5exf4A7/ISqoKp+sL1MxdMjFt3I+8XS5Y="; hash = "sha256-WA75k8/SJkHd+qaLUJFy4uGM95wDn/18wv+awR0kTbo=";
}; };
flake = import "${src}/flake.nix"; flake = import "${src}/flake.nix";
evaluated = flake.outputs { evaluated = flake.outputs {
@@ -25,7 +25,7 @@ let
in src.overrideAttrs (base: { in src.overrideAttrs (base: {
# attributes required by update scripts # attributes required by update scripts
pname = "nixpkgs-wayland"; pname = "nixpkgs-wayland";
version = "0-unstable-2024-06-14"; version = "0-unstable-2024-06-21";
src = src; src = src;
# passthru only nixpkgs-wayland's own packages -- not the whole nixpkgs-with-nixpkgs-wayland-as-overlay: # passthru only nixpkgs-wayland's own packages -- not the whole nixpkgs-with-nixpkgs-wayland-as-overlay:

View File

@@ -27,12 +27,12 @@
}: }:
let let
lock = { lock = {
master.rev = "bac3e728035fee7d7fd3df57e7bb89298d1b1b2e"; master.rev = "124ae37067e998cc9286daccf1f222d651c6bef5";
master.sha256 = "sha256-tW1Tzz/2SX8HiAXkQm6IbsRbzQuPH9W/ouoTt+bUGdk="; master.sha256 = "sha256-7GVAIzW3dgQrUpqrcOlZct8KnT+O9isobOvM/08fYIU=";
staging.rev = "bac3e728035fee7d7fd3df57e7bb89298d1b1b2e"; staging.rev = "124ae37067e998cc9286daccf1f222d651c6bef5";
staging.sha256 = "sha256-tW1Tzz/2SX8HiAXkQm6IbsRbzQuPH9W/ouoTt+bUGdk="; staging.sha256 = "sha256-7GVAIzW3dgQrUpqrcOlZct8KnT+O9isobOvM/08fYIU=";
staging-next.rev = "bac3e728035fee7d7fd3df57e7bb89298d1b1b2e"; staging-next.rev = "124ae37067e998cc9286daccf1f222d651c6bef5";
staging-next.sha256 = "sha256-tW1Tzz/2SX8HiAXkQm6IbsRbzQuPH9W/ouoTt+bUGdk="; staging-next.sha256 = "sha256-7GVAIzW3dgQrUpqrcOlZct8KnT+O9isobOvM/08fYIU=";
}; };
lock' = lock."${variant}"; lock' = lock."${variant}";
unpatchedSrc = fetchzip { unpatchedSrc = fetchzip {
@@ -81,7 +81,7 @@ in
src.overrideAttrs (base: { src.overrideAttrs (base: {
# attributes needed for update scripts # attributes needed for update scripts
pname = "nixpkgs"; pname = "nixpkgs";
version = "24.05-unstable-2024-06-15"; version = "24.05-unstable-2024-06-21";
passthru = (base.passthru or {}) // nixpkgs // { passthru = (base.passthru or {}) // nixpkgs // {
src = unpatchedSrc // { src = unpatchedSrc // {
inherit (lock') rev; inherit (lock') rev;

View File

@@ -30,6 +30,14 @@ in
# hash = "sha256-fGuS46f9qSMRHvWZvTmcirKufIqlXHwwhckeK1RNejE="; # hash = "sha256-fGuS46f9qSMRHvWZvTmcirKufIqlXHwwhckeK1RNejE=";
# }) # })
(fetchpatch' {
# xsimd is required by scipy, which is required by ols
title = "xsimd: fix cross compilation";
# prUrl = "https://github.com/NixOS/nixpkgs/pull/321253";
prUrl = "https://github.com/NixOS/nixpkgs/pull/321288";
hash = "sha256-jeRM/dfnljX0i3lHsg8bPfQXO3+Wx9M+hSypJiG9MfE=";
})
(fetchpatch' { (fetchpatch' {
title = "unl0kr: 2.0.0 -> 3.2.0"; title = "unl0kr: 2.0.0 -> 3.2.0";
prUrl = "https://github.com/NixOS/nixpkgs/pull/319126"; prUrl = "https://github.com/NixOS/nixpkgs/pull/319126";
@@ -37,9 +45,9 @@ in
}) })
(fetchpatch' { (fetchpatch' {
title = "sysvol: init at 0-unstable-2024-06-07"; title = "syshud: init at 0-unstable-2024-06-20";
prUrl = "https://github.com/NixOS/nixpkgs/pull/318440"; prUrl = "https://github.com/NixOS/nixpkgs/pull/318440";
hash = "sha256-OX1OP2m9LJYjVcryhpt520XJmdK1XS0KKaEBzMjdpNo="; hash = "sha256-jVw5pC+f8Z68S/X9EvMB6nbwvoiR03qI7ALeLtkmN24=";
}) })
(fetchpatch' { (fetchpatch' {

View File

@@ -0,0 +1,32 @@
{ stdenv
, lib
, fetchurl
# database downloads are limited per API key, so please consider supplying your own API key if using this package
, apiKey ? "pk.758ba60a9bf5fc060451153c3e2542dc"
}:
stdenv.mkDerivation {
pname = "opencellid";
version = "0-unstable-2024-06-20";
src = fetchurl {
# this is a live url. updated daily? TODO: add an update script for this.
# the API key should allow for at least 2 downloads per day
url = "https://opencellid.org/ocid/downloads?token=${apiKey}&type=full&file=cell_towers.csv.gz";
hash = "sha256-uY9nHY/LHPJSqMjvIpRgiW++fzucRn1JwPdNXE63bq8=";
};
unpackPhase = ''
gunzip "$src" --stdout > cell_towers.csv
'';
installPhase = ''
cp cell_towers.csv $out
'';
meta = with lib; {
description = "100M-ish csv database of known celltower positions";
homepage = "https://opencellid.org";
maintainers = with maintainers; [ colinsane ];
};
}

View File

@@ -1,34 +1,54 @@
{ { stdenv ? null }:
findutils, with builtins;
runCommandLocal, let
rsync, src = filterSource
}: (path: type:
runCommandLocal "sane-nix-files" { let name = baseNameOf path;
nativeBuildInputs = [ in !(
findutils # mimic .gitignore
rsync (name == ".working")
]; || (name == "result")
|| (match "^result-.*" name != null)
))
../../../.
;
meta = { fakeDeriv = {
description = "top-level host configs for Colin's machines"; # in the bootstrap path, we don't have enough available to actually
longDescription = '' # link these files into a derivation.
i like to ensure a copy of my config is present on all my machines, # but that's ok, because the caller immediately `import`s it anyway,
and this does that in a hermetic way. # so just yield something importable.
''; outPath = src;
};
realDeriv = stdenv.mkDerivation {
name = "sane-nix-files";
inherit src;
installPhase = ''
ln -s "$src" "$out"
'';
dontFixup = true;
}; };
} ''
mkdir src
pushd src
rsync -lptr ${../../../.}/ ./ # alternative implementation which always returns a real derivation,
chmod u+w . # but requires a pre-compiled statically-linked `sln` or `cp` implementation.
for pat in $(cat .gitignore); do # self = derivation {
set +e # name = "sane-nix-files";
chmod u+w -R "$pat" ; rm -rf "$pat" # system = "x86_64-linux";
find $PWD -name "$pat" -exec 'chmod u+w -R {}; rm -rf {}' \;
set -e
done
rsync -lptr ./ $out/
popd # # builder = "${./sln}";
'' # # args = [
# # src
# # self.outPath
# # ];
# builder = "/bin/sh";
# args = [
# "-c"
# "${./sln} ${src} $out"
# ];
# };
in
if stdenv == null then
fakeDeriv
else
realDeriv

View File

@@ -35,6 +35,9 @@ configureKeyboardFor_application() {
# io.github.lainsce.Notejot.desktop) # io.github.lainsce.Notejot.desktop)
# setKeyboard showIfRoom # setKeyboard showIfRoom
# ;; # ;;
networkmanager_dmenu.desktop)
setKeyboard showIfRoom
;;
org.gnome.Epiphany.desktop) org.gnome.Epiphany.desktop)
setKeyboard showIfRoom setKeyboard showIfRoom
;; ;;

View File

@@ -12,6 +12,7 @@ usageDescription() {
echo "sane-vpn up [REGION]" echo "sane-vpn up [REGION]"
echo "sane-vpn down [REGION]" echo "sane-vpn down [REGION]"
echo "sane-vpn do [REGION [COMMAND ...] ]" echo "sane-vpn do [REGION [COMMAND ...] ]"
echo "sane-vpn do -- [COMMAND ...]"
echo "sane-vpn dns-fix" echo "sane-vpn dns-fix"
echo "sane-vpn help" echo "sane-vpn help"
} }
@@ -63,7 +64,7 @@ canonicalizeRegion() {
fi fi
done done
if [ -z "$region" ] || [ "$region" = "default" ]; then if [ -z "$region" ] || [ "$region" = "default" ] || [ "$region" = "-" ] || [ "$region" = "--" ]; then
debug "canonicalizing default region to '$defaultVpn'" debug "canonicalizing default region to '$defaultVpn'"
region="$defaultVpn" region="$defaultVpn"
fi fi

View File

@@ -1,5 +1,5 @@
{ static-nix-shell }: { static-nix-shell }:
static-nix-shell.mkPython3Bin { static-nix-shell.mkPython3Bin {
pname = "sane-sysinfo"; pname = "sane-sysload";
srcRoot = ./.; srcRoot = ./.;
} }

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env nix-shell #!/usr/bin/env nix-shell
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" #!nix-shell -i python3 -p "python3.withPackages (ps: [ ])"
""" """
usage: sane-sysinfo [options...] usage: sane-sysload [options...] formatstr
pretty-prints a battery estimate (icon to indicate state, and a duration estimate) pretty-prints a battery estimate (icon to indicate state, and a duration estimate)
@@ -11,10 +11,24 @@ options:
--hour-suffix <string>: use the provided string as an hours suffix --hour-suffix <string>: use the provided string as an hours suffix
--icon-suffix <string>: use the provided string as an icon suffix --icon-suffix <string>: use the provided string as an icon suffix
--percent-suffix <string>: use the provided string when displaying percents --percent-suffix <string>: use the provided string when displaying percents
formatstr is a Python format string.
variables available for formatting:
- {bat_icon}
- {bat_time}
- {cpu_icon}
- {cpu_pct}
- {mem_icon}
- {mem_pct}
and some presets, encapsulating the above:
- {bat}
- {cpu}
- {mem}
""" """
import argparse import argparse
import logging import logging
import time
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum from enum import Enum
@@ -24,6 +38,7 @@ logger = logging.getLogger(__name__)
# these icons may only render in nerdfonts # these icons may only render in nerdfonts
ICON_BAT_CHG = ["󰢟", "󱊤", "󱊥", "󰂅"] ICON_BAT_CHG = ["󰢟", "󱊤", "󱊥", "󰂅"]
ICON_BAT_DIS = ["󰂎", "󱊡", "󱊢", "󱊣"] ICON_BAT_DIS = ["󰂎", "󱊡", "󱊢", "󱊣"]
ICON_CPU=""
ICON_MEM="☵" ICON_MEM="☵"
SUFFIX_ICON = "" # thin space SUFFIX_ICON = "" # thin space
SUFFIX_PERCENT = "%" SUFFIX_PERCENT = "%"
@@ -72,6 +87,9 @@ class Formatter:
def render_charge_icon(self, direction: ChargeDirection, percentage: float) -> str: def render_charge_icon(self, direction: ChargeDirection, percentage: float) -> str:
return f"{self._choose_icon(direction, percentage)}{self.suffix_icon}" return f"{self._choose_icon(direction, percentage)}{self.suffix_icon}"
def render_cpu_icon(self) -> str:
return f"{ICON_CPU}{self.suffix_icon}"
def render_mem_icon(self) -> str: def render_mem_icon(self) -> str:
return f"{ICON_MEM}{self.suffix_icon}" return f"{ICON_MEM}{self.suffix_icon}"
@@ -137,6 +155,83 @@ class MemInfo:
logger.debug(f"/proc/meminfo: {entry}={v}") logger.debug(f"/proc/meminfo: {entry}={v}")
return v return v
class ProcStat:
"""
reads vaues from /proc/stat, mostly CPU-related.
"""
# /proc/stat format is documented here: <https://www.linuxhowtos.org/System/procstat.htm>
# these are AGGREGATES SINCE SYSTEM BOOT
# to measure current CPU usage, need to take multiple samples
# cpu <user> <system> <nice> <idle> <iowait> <irg> <softirq> 0 0 0
# (what are the last three fields?)
# where:
# measurements are in units of jiffies or USER_HZ
# user: normal processes executing in user mode
# nice: niced processes executing in user mode
# system: processes executing in kernel mode
# idle: twiddling thumbs
# iowait: waiting for I/O to complete
# irq: servicing interrupts
# softirq: servicing softirqs
def __init__(self, entries=None):
if entries is not None:
self.entries = entries
return
# else, read from procfs...
try:
lines = open("/proc/stat").readlines()
except Exception as e:
logger.info(f"failed to open /proc/stat: {e}")
lines = []
self.entries = {}
for l in lines:
pieces = l.strip().split(" ")
name, values = pieces[0], [p for p in pieces[1:] if p]
if name:
self.entries[name] = [int(v) for v in values]
@staticmethod
def sample(seconds: float = 1.0):
sample1 = ProcStat()
time.sleep(seconds)
sample2 = ProcStat()
return sample2 - sample1
def __sub__(self, other: 'ProcStat') -> 'ProcStat':
entries = {}
for k in self.entries:
entries[k] = [i - j for i, j in zip(self.entries[k], other.entries[k])]
return ProcStat(entries)
@property
def cpu_user(self) -> int:
return self.entries["cpu"][0]
@property
def cpu_system(self) -> int:
return self.entries["cpu"][1]
@property
def cpu_nice(self) -> int:
return self.entries["cpu"][2]
@property
def cpu_idle(self) -> int:
return self.entries["cpu"][3]
@property
def cpu_iowait(self) -> int:
return self.entries["cpu"][4]
@property
def cpu_irq(self) -> int:
return self.entries["cpu"][5]
@property
def cpu_softirq(self) -> int:
return self.entries["cpu"][6]
@property
def cpu_total(self) -> int:
# TODO: not sure if i'm supposed to include irq stuff here?
return self.cpu_user + self.cpu_system + self.cpu_nice + self.cpu_idle + self.cpu_iowait + self.cpu_irq + self.cpu_softirq
class PowerSupply: class PowerSupply:
""" """
reads values from /sys/class/power_supply/$dev/ API reads values from /sys/class/power_supply/$dev/ API
@@ -263,9 +358,39 @@ def try_all_batteries() -> BatteryInfo | None:
@dataclass @dataclass
class AllInfo: class AllInfo:
_fmt: Formatter _fmt: Formatter
_mem: MemInfo | None __bat: BatteryInfo | None = None
_bat: BatteryInfo | None __cpu: ProcStat | None = None
__mem: MemInfo | None = None
# lazy-loading
@property
def _bat(self):
if self.__bat is None:
self.__bat = try_all_batteries()
return self.__bat
@property
def _cpu(self):
if self.__cpu is None:
self.__cpu = ProcStat.sample()
return self.__cpu
@property
def _mem(self):
if self.__mem is None:
self.__mem = MemInfo()
return self.__mem
# user-facing format shorthands
@property
def bat(self) -> str:
return f"{self.bat_icon}{self.bat_time}"
@property
def cpu(self) -> str:
return f"{self.cpu_icon}{self.cpu_pct}"
@property
def mem(self) -> str:
return f"{self.mem_icon}{self.mem_pct}"
# manual/low-level fields
@property @property
def mem_icon(self) -> str: def mem_icon(self) -> str:
if self._mem is None: return "" if self._mem is None: return ""
@@ -307,6 +432,34 @@ class AllInfo:
else: else:
return self._fmt.render_percent(self._bat.percent_charged) return self._fmt.render_percent(self._bat.percent_charged)
@property
def cpu_icon(self) -> str:
if self._cpu is None: return ""
return self._fmt.render_cpu_icon()
@property
def cpu_pct(self) -> str:
if self._cpu is None:
return ""
idle = self._cpu.cpu_idle + self._cpu.cpu_iowait
total = self._cpu.cpu_total
cpu_use_pct = int((total - idle) / total * 100)
return self._fmt.render_percent(cpu_use_pct)
class LazyFormatter:
def __init__(self, obj: object, attr: str):
self.obj = obj
self.attr = attr
def __repr__(self) -> str:
return repr(getattr(self.obj, self.attr))
def __str__(self) -> str:
return str(getattr(self.obj, self.attr))
def main() -> None: def main() -> None:
logging.basicConfig() logging.basicConfig()
logging.getLogger().setLevel(logging.INFO) logging.getLogger().setLevel(logging.INFO)
@@ -317,8 +470,7 @@ def main() -> None:
parser.add_argument("--hour-suffix", default=SUFFIX_HR) parser.add_argument("--hour-suffix", default=SUFFIX_HR)
parser.add_argument("--minute-suffix", default=SUFFIX_MIN) parser.add_argument("--minute-suffix", default=SUFFIX_MIN)
parser.add_argument("--percent-suffix", default=SUFFIX_PERCENT) parser.add_argument("--percent-suffix", default=SUFFIX_PERCENT)
parser.add_argument("--template", default="{_.bat_icon}{_.bat_time}") parser.add_argument("formatstr", nargs="+")
# parser.add_argument("--template", default="{_.mem_icon}{_.mem_pct}")
args = parser.parse_args() args = parser.parse_args()
if args.debug: if args.debug:
@@ -330,12 +482,19 @@ def main() -> None:
f.suffix_hr = args.hour_suffix f.suffix_hr = args.hour_suffix
f.suffix_min = args.minute_suffix f.suffix_min = args.minute_suffix
info = AllInfo( info = AllInfo(f)
f, formatstr = " ".join(args.formatstr)
MemInfo(), print(formatstr.format(
try_all_batteries(), bat=LazyFormatter(info, "bat"),
) bat_icon=LazyFormatter(info, "bat_icon"),
print(args.template.format(_=info)) bat_time=LazyFormatter(info, "bat_time"),
cpu=LazyFormatter(info, "cpu"),
cpu_icon=LazyFormatter(info, "cpu_icon"),
cpu_pct=LazyFormatter(info, "cpu_pct"),
mem=LazyFormatter(info, "mem"),
mem_icon=LazyFormatter(info, "mem_icon"),
mem_pct=LazyFormatter(info, "mem_pct"),
))
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -6,8 +6,8 @@ let
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "Mic92"; owner = "Mic92";
repo = "sops-nix"; repo = "sops-nix";
rev = "c279dec105dd53df13a5e57525da97905cc0f0d6"; rev = "797ce4c1f45a85df6dd3d9abdc53f2691bea9251";
hash = "sha256-psA+1Q5fPaK6yI3vzlLINNtb6EeXj111zQWnZYyJS9c="; hash = "sha256-Pm9I/BMQHbsucdWf6y9G3xBZh3TMlThGo4KBbeoeczg=";
}; };
flake = import "${src}/flake.nix"; flake = import "${src}/flake.nix";
evaluated = flake.outputs { evaluated = flake.outputs {
@@ -21,7 +21,7 @@ in src.overrideAttrs (base: {
# attributes required by update scripts # attributes required by update scripts
pname = "sops-nix"; pname = "sops-nix";
# nix-update-script insists on this weird `assets-` version format # nix-update-script insists on this weird `assets-` version format
version = "assets-unstable-2024-06-11"; version = "assets-unstable-2024-06-16";
src = src; src = src;
passthru = base.passthru passthru = base.passthru

View File

@@ -107,6 +107,7 @@ let
xdotool xdotool
] ++ lib.optionals preferXdgOpen [ xdg-utils ]; ] ++ lib.optionals preferXdgOpen [ xdg-utils ];
in in
lib.warn "sxmo-utils from nur.colinsane is no longer maintained and will be removed in the future. consider pointing to a different upstream or copying the package into your own config."
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "sxmo-utils"; pname = "sxmo-utils";
version = "unstable-2024-02-05"; version = "unstable-2024-02-05";

View File

@@ -8,14 +8,14 @@
, wrapGAppsHook4 , wrapGAppsHook4
}: }:
stdenv.mkDerivation { stdenv.mkDerivation {
pname = "sysvol"; pname = "syshud";
version = "0-unstable-2024-06-13"; version = "0-unstable-2024-06-20";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "System64fumo"; owner = "System64fumo";
repo = "sysvol"; repo = "syshud";
rev = "af882d40df7c6e1a0ec415d934f643933f455b5a"; rev = "2b97f3441970efe67c788a8313eb58182aa7965b";
hash = "sha256-3gB1u7fEi7EB+FWZKS8ddJ53RC5Chyw3fTBX8Z0Itis="; hash = "sha256-XPAKjBLaTGEyDgiZT8tYinYzMivOocOEeauzR4leOjI=";
}; };
postPatch = '' postPatch = ''
substituteInPlace Makefile \ substituteInPlace Makefile \
@@ -40,9 +40,9 @@ stdenv.mkDerivation {
}; };
meta = { meta = {
description = "A basic GTK4 volume indicator"; description = "Simple heads up display written in gtkmm 4";
homepage = "https://github.com/System64fumo/sysvol"; homepage = "https://github.com/System64fumo/syshud";
mainProgram = "sysvol"; mainProgram = "syshud";
platforms = lib.platforms.linux; platforms = lib.platforms.linux;
maintainers = with lib.maintainers; [ colinsane ]; maintainers = with lib.maintainers; [ colinsane ];
}; };

View File

@@ -5,12 +5,12 @@
}: }:
stdenv.mkDerivation { stdenv.mkDerivation {
pname = "uassets"; pname = "uassets";
version = "0-unstable-2024-06-15"; version = "0-unstable-2024-06-21";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "uBlockOrigin"; owner = "uBlockOrigin";
repo = "uAssets"; repo = "uAssets";
rev = "bbb9bd592fe54dc2cbc952a68dd715bb481ab83d"; rev = "05dfb7b5504b20e5432f7092111ad51568b3d9f4";
hash = "sha256-r7ZCfHxCNVETu1X7O8vb4xkxTAmYVWFDTHQOZeQ8C2I="; hash = "sha256-FrSMmZxR+XWUUOb8X1kImEM4Lhw8Ikg8k1vtqXOJD+8=";
}; };
dontBuild = true; dontBuild = true;

View File

@@ -1,9 +0,0 @@
{ linkFarm
, geoclue2
}:
linkFarm "where-am-i" [{
# bring the `where-am-i` tool into a `bin/` directory so it can be invokable via PATH
name = "bin/where-am-i";
path = "${geoclue2}/libexec/geoclue-2.0/demos/where-am-i";
}]

View File

@@ -71,6 +71,7 @@ let
nixpkgs-staging = nixpkgs.override { variant = "staging"; }; nixpkgs-staging = nixpkgs.override { variant = "staging"; };
nixpkgs-next = nixpkgs.override { variant = "staging-next"; }; nixpkgs-next = nixpkgs.override { variant = "staging-next"; };
nixpkgs-wayland = callPackage ./additional/nixpkgs-wayland { }; nixpkgs-wayland = callPackage ./additional/nixpkgs-wayland { };
opencellid = callPackage ./additional/opencellid { };
peerswap = callPackage ./additional/peerswap { }; peerswap = callPackage ./additional/peerswap { };
phog = callPackage ./additional/phog { }; phog = callPackage ./additional/phog { };
pipeline = callPackage ./additional/pipeline { }; pipeline = callPackage ./additional/pipeline { };
@@ -84,7 +85,7 @@ let
sane-open = callPackage ./additional/sane-open { }; sane-open = callPackage ./additional/sane-open { };
sane-screenshot = callPackage ./additional/sane-screenshot { }; sane-screenshot = callPackage ./additional/sane-screenshot { };
sane-scripts = lib.recurseIntoAttrs (callPackage ./additional/sane-scripts { }); sane-scripts = lib.recurseIntoAttrs (callPackage ./additional/sane-scripts { });
sane-sysinfo = callPackage ./additional/sane-sysinfo { }; sane-sysload = callPackage ./additional/sane-sysload { };
sane-weather = callPackage ./additional/sane-weather { }; sane-weather = callPackage ./additional/sane-weather { };
sanebox = callPackage ./additional/sanebox { }; sanebox = callPackage ./additional/sanebox { };
schlock = callPackage ./additional/schlock { }; schlock = callPackage ./additional/schlock { };
@@ -97,7 +98,7 @@ let
sxmo_swaylock = callPackage ./additional/sxmo_swaylock { }; sxmo_swaylock = callPackage ./additional/sxmo_swaylock { };
sxmo-suspend = callPackage ./additional/sxmo-suspend { }; sxmo-suspend = callPackage ./additional/sxmo-suspend { };
sxmo-utils = callPackage ./additional/sxmo-utils { }; sxmo-utils = callPackage ./additional/sxmo-utils { };
sysvol = callPackage ./additional/sysvol { }; syshud = callPackage ./additional/syshud { };
tow-boot-pinephone = callPackage ./additional/tow-boot-pinephone { }; tow-boot-pinephone = callPackage ./additional/tow-boot-pinephone { };
tree-sitter-nix-shell = callPackage ./additional/tree-sitter-nix-shell { }; tree-sitter-nix-shell = callPackage ./additional/tree-sitter-nix-shell { };
trivial-builders = lib.recurseIntoAttrs (callPackage ./additional/trivial-builders { }); trivial-builders = lib.recurseIntoAttrs (callPackage ./additional/trivial-builders { });
@@ -113,7 +114,6 @@ let
runCommandLocalOverridable runCommandLocalOverridable
; ;
unftp = callPackage ./additional/unftp { }; unftp = callPackage ./additional/unftp { };
where-am-i = callPackage ./additional/where-am-i { };
zecwallet-light-cli = callPackage ./additional/zecwallet-light-cli { }; zecwallet-light-cli = callPackage ./additional/zecwallet-light-cli { };
# packages i haven't used for a while, may or may not still work # packages i haven't used for a while, may or may not still work
@@ -162,6 +162,20 @@ let
# modemmanager = callPackage ./patched/modemmanager { inherit (unpatched) modemmanager; }; # modemmanager = callPackage ./patched/modemmanager { inherit (unpatched) modemmanager; };
playerctl = unpatched.playerctl.overrideAttrs (upstream: {
patches = (upstream.patches or []) ++ [
(fetchpatch {
# playerctl, when used as a library, doesn't expect its user to `unref` it inside a glib signal.
# nwg-panel does this though, and then segfaults.
# playerctl project looks dead as of 2024/06/19, no hope for upstreaming this.
# TODO: consider removing this if nwg-panel code is changed to not trigger this.
# - <https://github.com/nwg-piotr/nwg-panel/issues/233>
name = "dbus_name_owner_changed_callback: acquire a ref on the manager before using it";
url = "https://git.uninsane.org/colin/playerctl/commit/bbcbbe4e03da93523b431ffee5b64e10b17b4f9f.patch";
hash = "sha256-l/w+ozga8blAB2wtEd1SPBE6wpHNXWk7NrOL7x10oUI=";
})
];
});
### PYTHON PACKAGES ### PYTHON PACKAGES
pythonPackagesExtensions = (unpatched.pythonPackagesExtensions or []) ++ [ pythonPackagesExtensions = (unpatched.pythonPackagesExtensions or []) ++ [

View File

@@ -2,6 +2,8 @@
{ {
depthcharge-tools = callPackage ./depthcharge-tools { }; depthcharge-tools = callPackage ./depthcharge-tools { };
feedsearch-crawler = callPackage ./feedsearch-crawler { }; feedsearch-crawler = callPackage ./feedsearch-crawler { };
fastcluster = callPackage ./fastcluster { };
ols = callPackage ./ols { };
pa-dlna = callPackage ./pa-dlna { }; pa-dlna = callPackage ./pa-dlna { };
pyln-bolt7 = callPackage ./pyln-bolt7 { }; pyln-bolt7 = callPackage ./pyln-bolt7 { };
pyln-client = callPackage ./pyln-client { }; pyln-client = callPackage ./pyln-client { };

View File

@@ -0,0 +1,38 @@
{ lib
, buildPythonPackage
, fetchFromGitHub
, oldest-supported-numpy
, numpy
, setuptools
}: buildPythonPackage rec {
pname = "fastcluster";
version = "1.2.6";
format = "pyproject";
# format = "setuptools";
src = fetchFromGitHub {
owner = "fastcluster";
repo = "fastcluster";
rev = "v${version}";
hash = "sha256-8FDipkAcOAI5zAC7JaJExe6HO1xHg+eXAL7IUIVrA3k=";
};
nativeBuildInputs = [
oldest-supported-numpy
setuptools
];
# propagatedBuildInputs = [
# numpy
# ];
pythonImportsCheck = [
"fastcluster"
];
meta = with lib; {
homepage = "https://danifold.net/fastcluster.html";
description = "fast hierarchical clustering routines for R and Python";
maintainers = with maintainers; [ colinsane ];
};
}

View File

@@ -0,0 +1,60 @@
{ lib
, buildPythonPackage
, fetchFromGitea
, aiohttp
, fastcluster
, fastjsonschema
, mercantile
, numpy
, scipy
, setuptools
# , wheel
}: buildPythonPackage {
pname = "ols";
version = "0.1.0-unstable-2024-06-21";
format = "pyproject";
# format = "setuptools";
src = fetchFromGitea {
# my dev branch has a few changes:
# - fix `cellid-ols-import` to make --mcc, --mnc actually be optional
# - synthesize cell locations when no exact match is found
domain = "git.uninsane.org";
owner = "colin";
repo = "ols";
rev = "2caacd27a6253f711d0820ae51e6fe178bd80343";
hash = "sha256-vzvEraBi71xz1rjQWFRFKkAaVO9ASNQ2dTvT6y+xihI=";
};
# src = fetchFromGitea {
# domain = "codeberg.org";
# owner = "tpikonen";
# repo = "ols";
# rev = "069560accc6558f16d6d9abea63bd7563ea3f0e9";
# hash = "sha256-/931fc37QzITOA61D2CeXr/JmvDg0t8fLSt2y+2kSyQ=";
# };
# src = /home/colin/ref/repos/tpikonen/ols;
propagatedBuildInputs = [
aiohttp
fastcluster
fastjsonschema
mercantile
numpy
scipy
];
nativeBuildInputs = [
setuptools
# wheel
];
pythonImportsCheck = [
"ols"
];
meta = with lib; {
homepage = "https://codeberg.org/tpikonen/ols";
description = "HTTP location service with Mozilla Location Service (MLS) compatible API";
maintainers = with maintainers; [ colinsane ];
};
}

View File

@@ -1,7 +1,8 @@
#!/bin/sh #!/bin/sh
cd ../integrations/nur NIX_FILES_TOP=/home/colin/nixos
cd $NIX_FILES_TOP/integrations/nur
# TODO: should include `-I nixpkgs=</path/to/an/unpatched/nixpkgs>` # TODO: should include `-I nixpkgs=</path/to/an/unpatched/nixpkgs>`
NIX_PATH= NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM=1 nix-env -f . -qa \* --meta --xml \ NIX_PATH= NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM=1 nix-env -f . -qa linux\* --meta --xml \
--allowed-uris https://static.rust-lang.org \ --allowed-uris https://static.rust-lang.org \
--option restrict-eval true \ --option restrict-eval true \
--option allow-import-from-derivation true \ --option allow-import-from-derivation true \

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env nix-shell #!/usr/bin/env nix-shell
#!nix-shell -i bash -p curl -p dig -p iputils -p lftp -p openssh #!nix-shell -i bash -p curl -p dig -p iputils -p openssh
echo "this script will check that uninsane.org is baseline operational" echo "this script will check that uninsane.org is baseline operational"
echo "it doesn't check all services, just the most critical ones" echo "it doesn't check all services, just the most critical ones"
@@ -8,12 +8,40 @@ echo ""
OVPNS_IPV4=185.157.162.178 OVPNS_IPV4=185.157.162.178
DOOF_IPV4=205.201.63.12 DOOF_IPV4=205.201.63.12
usage() {
echo "usage: check-uninsane [flags ...]"
echo "flags:"
echo "- --verbose: show commands before running them"
exit 1
}
verbose=
parseArgs() {
while [ "$#" -ne 0 ]; do
local arg=$1
shift
case $arg in
(--verbose)
verbose=1
;;
(*)
usage
;;
esac
done
}
last_error=0 last_error=0
check() { check() {
local label=$1 local label=$1
shift shift
if [ -n "$verbose" ]; then
printf "checking %s (%s) \n" "$label" "$*"
"$@"
else
printf "checking %s\n" "$label" printf "checking %s\n" "$label"
"$@" > /dev/null "$@" > /dev/null
fi
local rc=$? local rc=$?
if [ $rc -ne 0 ]; then if [ $rc -ne 0 ]; then
last_error=$rc last_error=$rc
@@ -23,6 +51,16 @@ check() {
return $rc return $rc
} }
runOnHost() {
local host="$1"
shift
if [ "$host" = "$(hostname)" ]; then
"$@"
else
ssh "$host-hn" "$@"
fi
}
check "self-test" false 2> /dev/null check "self-test" false 2> /dev/null
if [ $last_error -eq 0 ]; then if [ $last_error -eq 0 ]; then
echo "SELF-TEST FAILED" >&2 echo "SELF-TEST FAILED" >&2
@@ -31,10 +69,12 @@ if [ $last_error -eq 0 ]; then
fi fi
last_error=0 last_error=0
parseArgs "$@"
check "uninsane.org. DNS" nslookup uninsane.org. check "uninsane.org. DNS" nslookup uninsane.org.
check "uninsane.org. DNS via external resolver" nslookup uninsane.org. 1.1.1.1 check "[1.1.1.1] uninsane.org. DNS" nslookup uninsane.org. 1.1.1.1
check "uninsane.org. bootstrap DNS" nslookup uninsane.org. ovpns.uninsane.org check "[OVPNS] uninsane.org. DNS" nslookup uninsane.org. "$OVPNS_IPV4"
check "[DOOF] uninsane.org. DNS" nslookup uninsane.org. "$DOOF_IPV4"
check "https://uninsane.org online" curl --silent --fail-with-body https://uninsane.org check "https://uninsane.org online" curl --silent --fail-with-body https://uninsane.org
check "https://matrix.uninsane.org online" curl --silent --fail-with-body https://matrix.uninsane.org check "https://matrix.uninsane.org online" curl --silent --fail-with-body https://matrix.uninsane.org
@@ -53,13 +93,14 @@ check "uninsane.org DMARC record" nslookup -querytype=TXT _dmarc.uninsane.org.
check "servo-hn wireguard network" ping -c 1 -W 3 servo-hn check "servo-hn wireguard network" ping -c 1 -W 3 servo-hn
check "git.uninsane.org" git ls-remote origin --quiet check "git.uninsane.org" git ls-remote https://git.uninsane.org/colin/nix-files.git --quiet
check "ftp://uninsane.org" lftpget ftp://uninsane.org/README.md && rm README.md check "ftps://ftp.uninsane.org" curl --silent ftps://ftp.uninsane.org/pub/test
check "[DOOF] ftps://ftp.uninsane.org" curl "--connect-to" "uninsane.org:80:$DOOF_IPV4:80" --silent ftps://ftp.uninsane.org/pub/test
echo "" echo ""
echo "systemctl --failed:" echo "systemctl --failed:"
ssh uninsane.org systemctl -q --failed runOnHost servo systemctl -q --failed
echo "" echo ""
if [ $last_error -eq 0 ]; then if [ $last_error -eq 0 ]; then

View File

@@ -9,8 +9,10 @@ usage() {
echo "usage: deploy [options] [host] [host2 ...]" echo "usage: deploy [options] [host] [host2 ...]"
echo "options:" echo "options:"
echo "- --action copy|switch|test (default: 'switch')" echo "- --action copy|switch|test (default: 'switch')"
echo "- --variant light|min|''|all (default: '')" echo "- --dry-run: show what would be done without actually doing it"
echo "- --pre: alias for --action copy --variant all all" echo "- --pre: alias for --action copy --variant all all"
echo "- --reboot: reboot the target machine after deploying (whether deployment was 'successful' or not)"
echo "- --variant light|min|''|all (default: '')"
echo "" echo ""
echo "common idioms:" echo "common idioms:"
echo "- deploy all: deploy all hosts, sequentially" echo "- deploy all: deploy all hosts, sequentially"
@@ -30,6 +32,8 @@ defaultHost="$SELF"
variants=() variants=()
defaultVariant= defaultVariant=
nixArgs=() nixArgs=()
doReboot=
dryRun=
addHost() { addHost() {
if [ "$1" = all ]; then if [ "$1" = all ]; then
# order matters: # order matters:
@@ -57,9 +61,20 @@ parseArgs() {
action=$1 action=$1
shift shift
;; ;;
(--dry-run)
dryRun=1
;;
(--help) (--help)
usage usage
;; ;;
(--pre)
action=copy
defaultVariant=all
defaultHost=all
;;
(--reboot)
doReboot=1
;;
(--variant) (--variant)
addVariant "$1" addVariant "$1"
shift shift
@@ -67,11 +82,6 @@ parseArgs() {
(all|crappy|desko|lappy|moby|servo) (all|crappy|desko|lappy|moby|servo)
addHost "$arg" addHost "$arg"
;; ;;
(--pre)
action=copy
defaultVariant=all
defaultHost=all
;;
(*) (*)
nixArgs+=("$arg") nixArgs+=("$arg")
;; ;;
@@ -86,14 +96,24 @@ parseArgs() {
fi fi
} }
destructive() {
if [ -z "$dryRun" ]; then
"$@"
else
echo "dry-run: $@"
fi
}
runOnTarget() { runOnTarget() {
local host="$1"
shift
# run the command ($@) on the machine we're deploying to. # run the command ($@) on the machine we're deploying to.
# if that's a remote machine, then do it via ssh, else local shell. # if that's a remote machine, then do it via ssh, else local shell.
if [ -n "$host" ] && [ "$host" != "$SELF" ]; then if [ -n "$host" ] && [ "$host" != "$SELF" ]; then
info "running on remote:" "$@" info "running on remote ($host):" "$@"
ssh "$host" "$@" ssh "$host" "$@"
else else
info "running locally:" "$@" info "running locally ($SELF):" "$@"
"$@" "$@"
fi fi
} }
@@ -104,7 +124,7 @@ deployOneHost() {
local variant="$2" local variant="$2"
info "building $host$variant ..." info "building $host$variant ..."
nix-build -A "hosts.$host$variant.toplevel" --out-link "./build/result-$host$variant" "${nixArgs[@]}" || return 1 destructive nix-build -A "hosts.$host$variant.toplevel" --out-link "./build/result-$host$variant" "${nixArgs[@]}" || return 1
storePath="$(readlink ./build/result-$host$variant)" storePath="$(readlink ./build/result-$host$variant)"
info "build $host$variant -> $storePath" info "build $host$variant -> $storePath"
@@ -120,20 +140,24 @@ deployOneHost() {
if [ -n "$host" ] && [ "$host" != "$SELF" ]; then if [ -n "$host" ] && [ "$host" != "$SELF" ]; then
if [ -e /run/secrets/nix_signing_key ]; then if [ -e /run/secrets/nix_signing_key ]; then
info "signing store paths ..." info "signing store paths ..."
sudo nix store sign -r -k /run/secrets/nix_signing_key "$storePath" destructive sudo nix store sign -r -k /run/secrets/nix_signing_key "$storePath"
else else
info "not signing store paths: /run/secrets/nix_signing_key does not exist" info "not signing store paths: /run/secrets/nix_signing_key does not exist"
fi fi
# add more `-v` for more verbosity (up to 5). # add more `-v` for more verbosity (up to 5).
# builders-use-substitutes false: optimizes so that the remote machine doesn't try to get paths from its substituters. # builders-use-substitutes false: optimizes so that the remote machine doesn't try to get paths from its substituters.
# we already have all paths here, and the remote substitution is slow to check and SERIOUSLY flaky on moby in particular. # we already have all paths here, and the remote substitution is slow to check and SERIOUSLY flaky on moby in particular.
nix copy -vv --option builders-use-substitutes false --to "ssh-ng://$host" "$storePath" || return 1 destructive nix copy -vv --option builders-use-substitutes false --to "ssh-ng://$host" "$storePath" || return 1
fi fi
if [ -n "$action" ] && [ "$action" != "copy" ]; then if [ -n "$action" ] && [ "$action" != "copy" ]; then
info "activating profile... " info "activating profile... "
runOnTarget sudo nix-env -p /nix/var/nix/profiles/system --set "$storePath" || return 1 destructive runOnTarget "$host" sudo nix-env -p /nix/var/nix/profiles/system --set "$storePath" || return 1
runOnTarget sudo "$storePath/bin/switch-to-configuration" "$action" || return 1 destructive runOnTarget "$host" sudo "$storePath/bin/switch-to-configuration" "$action" || return 1
if [ -n "$doReboot" ]; then
info "rebooting $host"
destructive runOnTarget "$host" sane-reboot
fi
fi fi
} }
@@ -141,8 +165,8 @@ deployOneHost() {
parseArgs "$@" parseArgs "$@"
failedDeploys=() failedDeploys=()
for h in "${hosts[@]}"; do for v in "${variants[@]}"; do
for v in "${variants[@]}"; do for h in "${hosts[@]}"; do
deployOneHost "$h" "$v" || \ deployOneHost "$h" "$v" || \
failedDeploys+=("$h$v") failedDeploys+=("$h$v")
done done

View File

@@ -1,6 +1,8 @@
#!/usr/bin/env nix-shell #!/usr/bin/env nix-shell
#!nix-shell -i bash -p nix-update #!nix-shell -i bash -p nix-update
NIX_FILES_TOP=/home/colin/nixos
usage() { usage() {
echo "update: update rev/hash for one or more packages" echo "update: update rev/hash for one or more packages"
echo "usage: update [options] [attr-path]" echo "usage: update [options] [attr-path]"
@@ -44,34 +46,51 @@ getPkgs() {
attrPrefix=sane attrPrefix=sane
fi fi
local attrs="$(nix-env -f . --query --available --attr-path --no-name -A $attrPrefix)" # nix-env doesn't seem to build anything when evaluating queries,
# but since i use Import From Derivation along paths which i also want to query,
# then i need to ensure those derivations are available for import.
debug "creating requisite .drv store paths"
nix-instantiate -A nixpkgs "$NIX_FILES_TOP"
debug "querying attributes which match '$attrPrefix'"
local attrs=$(nix-env -f "$NIX_FILES_TOP" --query --available --attr-path --no-name -A "$attrPrefix" --show-trace)
debug "got: $attrs"
attrsArr+=($attrs) attrsArr+=($attrs)
} }
updateOnePkg() { updateOnePkg() {
local attrPath="$1" local attrPath="$1"
if [[ "$attrPath" =~ ^"$ignore" ]]; then if [ -n "$ignore" ] && [[ "$attrPath" =~ ^"$ignore" ]]; then
warn "ignoring $attrPath" warn "ignoring $attrPath"
return return
fi fi
local updateScript="$(nix eval --raw -f . $attrPath.passthru.updateScript --apply 'builtins.concatStringsSep "'" "'"')" local updateScript=$(nix eval --raw -f "$NIX_FILES_TOP" $attrPath.passthru.updateScript --apply 'builtins.concatStringsSep "'" "'"')
if [ -z "$updateScript" ]; then if [ -z "$updateScript" ]; then
warn "don't know how to update '$attrPath'" warn "don't know how to update '$attrPath'"
return return
fi fi
# make sure everything needed to invoke the script actually exists on disk # make sure everything needed to invoke the update script exists in-store
nix-build -A "$attrPath.passthru.updateScript" || true local context=$(nix eval --raw -f "$NIX_FILES_TOP" $attrPath.passthru.updateScript --apply 's: builtins.concatStringsSep " " (builtins.foldl'"'"' (acc: next: acc ++ next) [] (builtins.map builtins.attrNames (builtins.map builtins.getContext s)))')
for c in $context; do
debug "realizing updateScript requisite: $context"
nix-store --realize "$c" || true
done
local UPDATE_NIX_NAME="$(nix eval --raw -f . $attrPath.name)" local UPDATE_NIX_NAME=$(nix eval --raw -f "$NIX_FILES_TOP" $attrPath.name)
local UPDATE_NIX_PNAME="$(nix eval --raw -f . $attrPath.pname)" local UPDATE_NIX_PNAME=$(nix eval --raw -f "$NIX_FILES_TOP" $attrPath.pname)
local UPDATE_NIX_OLD_VERSION="$(nix eval --raw -f . $attrPath.version)" local UPDATE_NIX_OLD_VERSION=$(nix eval --raw -f "$NIX_FILES_TOP" $attrPath.version)
info "updating: '$attrPath'" info "updating: '$attrPath'"
debug "$updateScript" debug "$updateScript"
(
# update script assumes $PWD is an entry point to a writable copy of my nix config,
# so provide that:
pushd "$NIX_FILES_TOP/integrations/nix-update"
# we lose spaces inside the exec args... could `nix eval` without `--raw` to fix that. # we lose spaces inside the exec args... could `nix eval` without `--raw` to fix that.
UPDATE_NIX_NAME="$UPDATE_NIX_NAME" UPDATE_NIX_PNAME="$UPDATE_NIX_PNAME" UPDATE_NIX_OLD_VERSION="$UPDATE_NIX_OLD_VERSION" UPDATE_NIX_ATTR_PATH="$attrPath" eval $updateScript UPDATE_NIX_NAME="$UPDATE_NIX_NAME" UPDATE_NIX_PNAME="$UPDATE_NIX_PNAME" UPDATE_NIX_OLD_VERSION="$UPDATE_NIX_OLD_VERSION" UPDATE_NIX_ATTR_PATH="$attrPath" eval $updateScript
popd
)
} }
dryRun= dryRun=

View File

@@ -1,5 +1,5 @@
{ {
"data": "ENC[AES256_GCM,data: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,iv:9WvW3DScrLPI3bEb7neJgWI/MDTvBfajWDtDj3rGxJs=,tag:VTDd0X5Oep/dSlZbjIO6tw==,type:str]", "data": "ENC[AES256_GCM,data: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,iv:jlCa29fKe/HWq3z17ymOrgYt50g3EjzOYYln6ywLWLg=,tag:MFtkrsTjtYCqZ+AYloffGQ==,type:str]",
"sops": { "sops": {
"kms": null, "kms": null,
"gcp_kms": null, "gcp_kms": null,
@@ -43,8 +43,8 @@
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwcXlNMlFUaWhXbmlPc0Ir\nT2hRd3BMQUZ1TkVkWGlrdjJ5ZnhPdWhPbTFBCmdzazV5bzV0aTNNaTFpaDlBOWU5\naUdBTFlEeWM5T1Q3UGhrZWdGZkVPUzQKLS0tIEloSFVJd2JCNmY0VkpqTGppRE1p\nQ2Mvekx0RWN5dlhxWmwwaDVnUUVDdFEKJgyAed3yBaIBwfxypG7RKV+u80SiQV89\ntU4YMw0j7GvnkVhPenB8q0w3yrslCh35GDvKkaMpfp8jVFIpcrRXtA==\n-----END AGE ENCRYPTED FILE-----\n" "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwcXlNMlFUaWhXbmlPc0Ir\nT2hRd3BMQUZ1TkVkWGlrdjJ5ZnhPdWhPbTFBCmdzazV5bzV0aTNNaTFpaDlBOWU5\naUdBTFlEeWM5T1Q3UGhrZWdGZkVPUzQKLS0tIEloSFVJd2JCNmY0VkpqTGppRE1p\nQ2Mvekx0RWN5dlhxWmwwaDVnUUVDdFEKJgyAed3yBaIBwfxypG7RKV+u80SiQV89\ntU4YMw0j7GvnkVhPenB8q0w3yrslCh35GDvKkaMpfp8jVFIpcrRXtA==\n-----END AGE ENCRYPTED FILE-----\n"
} }
], ],
"lastmodified": "2023-10-28T10:34:31Z", "lastmodified": "2024-06-23T17:59:53Z",
"mac": "ENC[AES256_GCM,data:GS492dizmvoW6uCO4Wz+YzwFbtM6LfUdEgx7DzSbHG3hM/q3k5LW8E4HzxkBkDUylL5GS3FUWCADcoKe+JWqX3TUqQDqqrYScAVcE5e9TM2mZ12U6xz1SU8Tc5DkqQuUgrv/pvaJBI1UlEEdFkSOSIBzHZ5C7xW0WsNFauC9HFI=,iv:rnDBUWzieefIpES4WjWWEiAY3LtKkfazAgHaSoJ9VEw=,tag:n4b6kctXHHKiQfQAeQeFTw==,type:str]", "mac": "ENC[AES256_GCM,data:ZKGwqisn27YnSjroCxrCxIoR5vkYgnfzVoLNbxfnti+RFUX67eBfcj0jOc8KqfAWyNOCMlWlfOZ1AonIOqsBqhPrX1LEyOdhrCp+M7zfhnIheOJFfqVyUzN0O7N0PIj696tM8HgYg2OX+YLCvZvFf9DSrK92RsRCkWSfH34OKd8=,iv:AByoesqgTOoet2BR01d6G1CBx0o/PDGj2kbxNU5St20=,tag:Um2vz1mdD5gu4CifY4QuTA==,type:str]",
"pgp": null, "pgp": null,
"unencrypted_suffix": "_unencrypted", "unencrypted_suffix": "_unencrypted",
"version": "3.8.1" "version": "3.8.1"

View File

@@ -0,0 +1,52 @@
{
"data": "ENC[AES256_GCM,data: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,iv:jbrfq9CYDgVQUK3od8eFjm6+KOTcypeTF3itq72XBBE=,tag:1jbIG1GaI0pSxIsErUXoBg==,type:str]",
"sops": {
"kms": null,
"gcp_kms": null,
"azure_kv": null,
"hc_vault": null,
"age": [
{
"recipient": "age1tnl4jfgacwkargzeqnhzernw29xx8mkv73xh6ufdyde6q7859slsnzf24x",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwQU9melI3VDB1aEtOYW5E\nbDRDL1BWMExsbjlxckFYZWtWMTlPZ29yR25BCjV5a0VyNHdQbXpMVGljZENxM2hl\nRlBRYTZoWnE4VVNQb05MTm1pcGsrVk0KLS0tIFFhOTE2S1luLzMxWnlmbGlzbEh5\nS1dFZU1JRG8vdDFjL3g5eEJ3ckZKNEkK1S1FMhvKCNbWlmDdOIgnn3+uAcK9bs9G\nvlfoV6xzAdAJDlckPLDipfS0x6HKUkN9PdA2K+SYJJ9673xegZ/xeQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1j2pqnl8j0krdzk6npe93s4nnqrzwx978qrc0u570gzlamqpnje9sc8le2g",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHWkZva1JreGpGWTBzcVBz\nSXlxakpjVk5ycndQRnUzYm9ZMXRXcFZTK3c0CmRBUFowNDlTMEgwVkpkOEQvNms4\nY2NRZ3g2SnJhdEJjbDlKZzlmVjFFL1EKLS0tIEFlL3VoUGJUaGY2SzFPT09QSXNS\ndElTQ2ZnNzJIRG51NmNjWldZYVFLV0EKOMJvXC9HjtNDuJELLfbByES0yVpliAnq\n7sWQRkY3K/J/F6cW5++LepZWMmMY7YmnKzIphreBeZncYUYx4JuzUQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1z8fauff34cdecr6sjkre260luzxcca05kpcwvhx988d306tpcejsp63znu",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTMmVSU1ByanlMYUR4TlFN\nYml4SjU4b1ZRR3VDazFlN3p2ZndacFZXUmo0CkhhTWtzVyszaUR4dG9nWmhoSHFn\nSWNtaGN2SzhtVWNkSkdUUEdqWmNyWTQKLS0tIHJnblV3Z082dVNLRUtNN216eTJL\nRXlKRVd0UGE3SE1rdUQ1NVlCSUFjL1UKf6Y6lvki5rFjqrPCLCL8SulHh3yg0Cns\nDdMsZgqLql1O3Cua4CHjSi+08hsz0RT9qqzcNr798Bqj5JnvFSTUPw==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1zsrsvd7j6l62fjxpfd2qnhqlk8wk4p8r0dtxpe4sdgnh2474095qdu7xj9",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpZGs5MHZqOFVoSzQxdEFC\nem1yVzZCditGekRMYzFCcWsvVk0vYlBubEJVCmV2ZHZqR3BMTW9Pa1RGWXVFdkV4\nbnNlKy9PUSswTVlETmxjMDAxakp2cmsKLS0tIFJDZlZzYmwwaFlua2FGeTdTUGtZ\nUXNoVFpwT21uR0NpcTFzZEgzby9YR0UKmZFKfm6ypb9SZBW0vTtw6SDL6TgZnsp3\ns41G4S4PS/8XyAHKrLlgGiJK/Fv3bMULG54D2TKQSbuYRhKXT9nWoA==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1hl50ufuxnqy0jnk8fqeu4tclh4vte2xn2d59pxff0gun20vsmv5sp78chj",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2Y3Frc2RleThrK1NmRTcx\nUkxvV29GNXllU0tXOWQ2YVkwSTloUjFCaUZFCk5iaVpJLy8vVUh5cGJBODVNWXNG\nRmtZYTZKK044S0VpSGdhSVBZZlZGY0kKLS0tIDg5c0IyTjNlU2FRYy9ueVYxbXIz\nZjZ0MGoweEtwQ2lUWDlqcEZvdThzYjQKMi0m7wyRoCoIbS/HBl6xaVEeK4TvYzkW\ny/rDK0IZuBAr9I1/avdDGM8LWo2EBSmqEqW6jooMmdrkkjwJE6lwVA==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1vnw7lnfpdpjn62l3u5nyv5xt2c965k96p98kc43mcnyzpetrts9q54mc9v",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMc0grUDVodGp5YWtpb1RS\nVUpweUUzcGxIbVpPYTJma3RaSDUwM2MvUlRJCmdacFE3OEQ1am53Q2hzYjRRaFRI\nSXlrbzVacDVlMnp1YlgrckExSG1yWTQKLS0tIE55R1QxOUJyN0N5Vmh1cG1iN2xC\nclNrWUF6TFRsTlBIUE9mNFZmQXNrMnMKVTrGgIrxA0pAjGOZyHJpwl6TBPBqXbGz\nUPgPFUUfLpUA+soyAQGE+/4bD4WFWwnro8B9zj0ahZYj1GvC8ddbig==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1w7mectcjku6x3sd8plm8wkn2qfrhv9n6zhzlf329e2r2uycgke8qkf9dyn",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtT3NIUE1ZeXN1ZnkxaXJ4\nUzJNRFZWMHg0WWNJeFJ0NUlITGMwYVlLajEwCkd4WGJXbjdwcnJPeDdrZEFyaDds\nZjR2eEtkc1ZpdzVFeG9LajBIcVVNTVEKLS0tIEwzd1ppa3EvSHNYdUhjcHVnZ3Fh\ncWJwUkZVM1QwTHp6a1N2MzVQcUFSeGMK0PFJNAkpQEKQiNarb9dDr7MwtvGZeke6\n7vBhMsJ1lmCMu0TshNNbOmQpre2J6ZnGDnvOrpy8IQukym2py9KvXQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1tzlyex2z6t88tg9h82943e39shxhmqeyr7ywhlwpdjmyqsndv3qq27x0rf",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzSkFLMnF0ZGdQV056VTdw\neUhyYnhGbzJORTh2ZlhXM0RzaWNjL3VybGlNCmNBVGlxSHIwWkxhZEg0ZXI3RFN0\nSk1XU0dTbFJ5NENGUnh0K3NRUCtzY1kKLS0tIGh2TnhuSERKb3B2c0oramhPelhm\nT1BBODhUaDVJbjg2Nlh3dnNPOUtvWmMKDyrOAsUZXp/hhXWczlf+VZRcz4mzaYZF\n3bW7w/nkojmMfKwKWvEYsunNPJSV+In1CTkvv8e8E1aIq5jithYe6Q==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age18vq5ktwgeaysucvw9t67drqmg5zd5c5k3le34yqxckkfj7wqdqgsd4ejmt",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGVmZEU2ozMjBNTmVMU1NG\ndFIzSndXcmZQVWphU1BWSURCVGhGQVhPUDE4CmZMS1dFU1BxL3FYQ25ETDJJWjhu\nTWwrd2VzemxoWGhJTzFXUzh4d0o2c2sKLS0tIEt1cmg1WHFQWjV6UlRvWEJhQVFh\nQjV0MU5zYm1US1Y0dUR1RldZeUxkcmcKgbhTfvnP+krgib2xcZ85szzH/EvgxAwH\nqKlmN5J8DmllxR+O97hwCdDMu3vC5Vx+lp7Rxs85xFHu2quw37liGw==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2024-06-21T13:47:00Z",
"mac": "ENC[AES256_GCM,data:vY0UHEkQKgWaevRo+9FxBjdSSFkAPMFt6ABSsZfArVqjBRLAo5O53aIV40KTDahd5UP25UgjPE8TVqXpWo/CVXct3M6SOnbSAlO9NGHkygyM5xk8PqzA6TXfPw7XzInvwMiAIuRNqJRAyKktHYmuiFMlEnhPhuCB6GLUdqpZEmQ=,iv:sK+QmTpl9y0UrruTqTb8oNcKrZrn9fgmldMxxyqr+vg=,tag:aeJeVIjwrcXAVOqMHNgIfQ==,type:str]",
"pgp": null,
"unencrypted_suffix": "_unencrypted",
"version": "3.8.1"
}
}