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?
- `ssh` access doesn't grant same linux capabilities as login
- 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
- `dmesg | grep "KASLR disabled due to lack of seed"`
- fix by adding `kaslrseed` to uboot script before `booti`
@@ -26,6 +26,11 @@
- `dmesg | grep 'hid_bpf: error while preloading HID BPF dispatcher: -22'`
- `s6` is not re-entrant
- so if the desktop crashes, the login process from `unl0kr` fails to re-launch the GUI
- swaync brightness slider does not work
- it reads brightness from /sys/class/backlight/....
- but to *set* the brightness it assumes systemd logind is running
<repo:ErikReider/SwayNotificationCenter:src/controlCenter/widgets/backlight/backlightUtil.vala>
no reason i can't just write to that file, or exec brightnessctl (if i learn vala)
## REFACTORING:
- add import checks to my Python nix-shell scripts
@@ -126,11 +131,9 @@
- add option to change audio output
- fix colors (red alert) to match overall theme
- moby: tune GPS
- run only geoclue, and not gpsd, to save power?
- tune QGPS setting in eg25-control, for less jitter?
- direct mepo to prefer gpsd, with fallback to geoclue, for better accuracy?
- configure geoclue to do some smoothing?
- manually do smoothing, as some layer between mepo and geoclue/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: show battery state on ssh login
- moby: improve gPodder launch time

View File

@@ -1,67 +1,5 @@
# limited, non-flake interface to this repo.
# this file exposes the same view into `pkgs` which the flake would see when evaluated.
#
# the primary purpose of this file is so i can run `updateScript`s which expect
# the root to be `default.nix`
{ }:
{ ... }@args:
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 (
[
(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 {})
sane-nix-files = import ./pkgs/additional/sane-nix-files { };
in
import "${sane-nix-files}/impure.nix" args

View File

@@ -25,6 +25,7 @@
sane.programs.dino.config.autostart = false;
sane.programs.dissent.config.autostart = false;
sane.programs.fractal.config.autostart = false;
sane.programs.sway.config.mod = "Mod1"; #< alt key instead of Super
# sane.programs.guiApps.enableFor.user.colin = false;

View File

@@ -10,7 +10,6 @@
{
imports = [
./fs.nix
./gps.nix
];
sane.hal.pine64.enable = true;
@@ -29,6 +28,7 @@
sops.secrets.colin-passwd.neededForUsers = true;
sane.programs.sway.enableFor.user.colin = true;
sane.programs.sway.config.mod = "Mod1"; #< alt key instead of Super
sane.programs.blueberry.enableFor.user.colin = false; # bluetooth manager: doesn't cross compile!
sane.programs.fcitx5.enableFor.user.colin = false; # does not cross compile
sane.programs.mercurial.enableFor.user.colin = false; # does not cross compile

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

View File

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

View File

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

View File

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

View File

@@ -12,6 +12,10 @@
device = "/var/media";
options = [ "rbind" ];
};
fileSystems."/var/export/pub" = {
device = "/var/www/sites/uninsane.org/share";
options = [ "rbind" ];
};
# fileSystems."/var/export/playground" = {
# device = config.fileSystems."/mnt/persist/ext".device;
# fsType = "btrfs";
@@ -37,7 +41,8 @@
wantedBy = [ "nfs.service" "sftpgo.service" ];
file.text = ''
- media/ read-only: Videos, Music, Books, etc
- playground/ read-write: use it to share files with other users of this server
- 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" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
# visibleTo.wan = true;
description = "colin-FTP server";
};
"990" = {
protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-FTPS server";
};
} // (sane-lib.mapToAttrs
@@ -41,8 +40,8 @@ in
name = builtins.toString port;
value = {
protocol = [ "tcp" ];
visibleTo.doof = true;
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-FTP server data port range";
};
})
@@ -101,6 +100,13 @@ in
debug = true;
tls_mode = 2; # 2 = "implicit FTPS": client negotiates TLS before any FTP command.
}
{
# binding this means any doof client can connect (TLS only)
address = config.sane.netns.doof.hostVethIpv4;
port = 990;
debug = true;
tls_mode = 2; # 2 = "implicit FTPS": client negotiates TLS before any FTP command.
}
];
# active mode is susceptible to "bounce attacks", without much benefit over passive mode
@@ -117,7 +123,7 @@ in
banner = ''
Welcome, friends, to Colin's FTP server! Also available via NFS on the same host, but LAN-only.
Read-only access (LAN-restricted):
Read-only access (LAN clients see everything; WAN clients can only see /pub):
Username: "anonymous"
Password: "anonymous"

View File

@@ -45,6 +45,8 @@ from hmac import compare_digest
authFail = dict(username="")
PERM_DENY = []
PERM_LIST = [ "list" ]
PERM_RO = [ "list", "download" ]
PERM_RW = [
# read-only:
@@ -127,12 +129,14 @@ def getAuthResponse(ip: str, username: str, password: str) -> dict:
return mkAuthOk(username, permissions = {
"/": PERM_RW,
"/playground": PERM_RW,
"/pub": PERM_RO,
})
if isWireguard(ip):
# allow any user from wireguard
return mkAuthOk(username, permissions = {
"/": PERM_RW,
"/playground": PERM_RW,
"/pub": PERM_RO,
})
if isLan(ip):
if username == "anonymous":
@@ -140,7 +144,19 @@ def getAuthResponse(ip: str, username: str, password: str) -> dict:
return mkAuthOk("anonymous", permissions = {
"/": PERM_RO,
"/playground": PERM_RW,
"/pub": PERM_RO,
})
if username == "anonymous":
# anonymous users from the www can have even more limited access.
# mostly because i need an easy way to test WAN connectivity :-)
return mkAuthOk("anonymous", permissions = {
# "/": PERM_DENY,
"/": PERM_LIST, #< REQUIRED, even for lftp to list a subdir
"/media": PERM_DENY,
"/playground": PERM_DENY,
"/pub": PERM_RO,
# "/README.md": PERM_RO, #< does not work
})
return authFail

View File

@@ -133,7 +133,7 @@
sane.ports.ports."22" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
visibleTo.doof = true;
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 = [
# 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 = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";
ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect 185.157.162.178" ]; # abort if public IP is not as expected
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
# 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;
# inherit kTLS;
locations."/" = {
# proxyPass = "http://ovpns.uninsane.org:9117";
proxyPass = "http://10.0.1.6:9117";
proxyPass = "http://${config.sane.netns.ovpns.netnsVethIpv4}:9117";
recommendedProxySettings = true;
};
};

View File

@@ -4,12 +4,11 @@
{ config, lib, ... }:
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;
in {
# XXX sasl: appservice doesn't support NickServ identification (only SASL, or PASS if sasl = false)
inherit name additionalAddresses sasl port;
ssl = true;
inherit additionalAddresses name port sasl ssl;
botConfig = {
# bot has no presence in IRC channel; only real Matrix users
enabled = false;
@@ -156,6 +155,10 @@ in
# - #sxmo-offtopic
};
"irc.rizon.net" = ircServer { name = "Rizon"; };
"wigle.net" = ircServer {
name = "WiGLE";
ssl = false;
};
};
};
};

View File

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

View File

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

View File

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

View File

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

View File

@@ -32,7 +32,7 @@
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://10.0.1.6:5030";
proxyPass = "http://${config.sane.netns.ovpns.netnsVethIpv4}:5030";
proxyWebsockets = true;
};
};
@@ -71,7 +71,7 @@
systemd.services.slskd.serviceConfig = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";
ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect 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
RestartSec = "60s";

View File

@@ -82,7 +82,6 @@ let
'';
};
in
lib.mkIf false #< TODO: re-enable once confident of sandboxing
{
sane.persist.sys.byStore.plaintext = [
# TODO: mode? we need this specifically for the stats tracking in .config/
@@ -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>
# message-level = 3; #< enable for debug logging. 0-3, default is 2.
# 10.0.1.6 => allow rpc only from the root servo ns. it'll tunnel things to the net, if need be.
rpc-bind-address = "10.0.1.6";
# ovpns.netnsVethIpv4 => allow rpc only from the root servo ns. it'll tunnel things to the net, if need be.
rpc-bind-address = config.sane.netns.ovpns.netnsVethIpv4;
#rpc-host-whitelist = "bt.uninsane.org";
#rpc-whitelist = "*.*.*.*";
rpc-authentication-required = true;
@@ -118,7 +117,7 @@ lib.mkIf false #< TODO: re-enable once confident of sandboxing
rpc-whitelist-enabled = false;
# 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;
# hopefully, make the downloads world-readable
@@ -160,7 +159,7 @@ lib.mkIf false #< TODO: re-enable once confident of sandboxing
systemd.services.transmission.serviceConfig = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";
ExecStartPre = [ "${lib.getExe pkgs.sane-scripts.ip-check} --no-upnp --expect 185.157.162.178" ]; # abort if public IP is not as expected
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";
RestartSec = "30s";
@@ -190,7 +189,7 @@ lib.mkIf false #< TODO: re-enable once confident of sandboxing
# inherit kTLS;
locations."/" = {
# 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
dyn-dns = config.sane.services.dyn-dns;
nativeAddrs = lib.mapAttrs (_name: builtins.head) config.sane.dns.zones."uninsane.org".inet.A;
bindOvpn = "10.0.1.5";
bindDoof = "10.0.2.5";
in
{
sane.ports.ports."53" = {
protocol = [ "udp" "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
# visibleTo.wan = true;
visibleTo.ovpns = true;
visibleTo.doof = true;
description = "colin-dns-hosting";
@@ -41,6 +39,7 @@ in
CNAME."native" = "%CNAMENATIVE%";
A."@" = "%ANATIVE%";
A."servo.wan" = "%AWAN%";
A."servo.doof" = "%ADOOF%";
A."servo.lan" = config.sane.hosts.by-name."servo".lan-ip;
A."servo.hn" = config.sane.hosts.by-name."servo".wg-home.ip;
@@ -48,9 +47,9 @@ in
# it's best that we keep this identical, or a superset of, what org. lists as our NS.
# so, org. can specify ns2/ns3 as being to the VPN, with no mention of ns1. we provide ns1 here.
A."ns1" = "%ANATIVE%";
A."ns2" = "185.157.162.178";
A."ns3" = "185.157.162.178";
A."ovpns" = "185.157.162.178";
A."ns2" = "%ADOOF%";
A."ns3" = "%AOVPNS%";
A."ovpns" = "%AOVPNS%";
NS."@" = [
"ns1.uninsane.org."
"ns2.uninsane.org."
@@ -61,59 +60,64 @@ in
services.trust-dns.settings.zones = [ "uninsane.org" ];
networking.nat.enable = true;
networking.nat.extraCommands = ''
# redirect incoming DNS requests from LAN addresses
# to the LAN-specialized DNS service
# N.B.: use the `nixos-*` chains instead of e.g. PREROUTING
# because they get cleanly reset across activations or `systemctl restart firewall`
# instead of accumulating cruft
iptables -t nat -A nixos-nat-pre -p udp --dport 53 \
-m iprange --src-range 10.78.76.0-10.78.79.255 \
-j DNAT --to-destination :1053
iptables -t nat -A nixos-nat-pre -p tcp --dport 53 \
-m iprange --src-range 10.78.76.0-10.78.79.255 \
-j DNAT --to-destination :1053
'';
sane.ports.ports."1053" = {
# because the NAT above redirects in nixos-nat-pre, LAN requests behave as though they arrived on the external interface at the redirected port.
# TODO: try nixos-nat-post instead?
# TODO: or, don't NAT from port 53 -> port 1053, but rather nat from LAN addr to a loopback addr.
# - this is complicated in that loopback is a different interface than eth0, so rewriting the destination address would cause the packets to just be dropped by the interface
protocol = [ "udp" "tcp" ];
visibleTo.lan = true;
description = "colin-redirected-dns-for-lan-namespace";
};
networking.nat.enable = true; #< TODO: try removing this?
# networking.nat.extraCommands = ''
# # redirect incoming DNS requests from LAN addresses
# # to the LAN-specialized DNS service
# # N.B.: use the `nixos-*` chains instead of e.g. PREROUTING
# # because they get cleanly reset across activations or `systemctl restart firewall`
# # instead of accumulating cruft
# iptables -t nat -A nixos-nat-pre -p udp --dport 53 \
# -m iprange --src-range 10.78.76.0-10.78.79.255 \
# -j DNAT --to-destination :1053
# iptables -t nat -A nixos-nat-pre -p tcp --dport 53 \
# -m iprange --src-range 10.78.76.0-10.78.79.255 \
# -j DNAT --to-destination :1053
# '';
# sane.ports.ports."1053" = {
# # because the NAT above redirects in nixos-nat-pre, LAN requests behave as though they arrived on the external interface at the redirected port.
# # TODO: try nixos-nat-post instead?
# # TODO: or, don't NAT from port 53 -> port 1053, but rather nat from LAN addr to a loopback addr.
# # - this is complicated in that loopback is a different interface than eth0, so rewriting the destination address would cause the packets to just be dropped by the interface
# protocol = [ "udp" "tcp" ];
# visibleTo.lan = true;
# description = "colin-redirected-dns-for-lan-namespace";
# };
sane.services.trust-dns.enable = true;
sane.services.trust-dns.instances = let
mkSubstitutions = flavor: {
"%ADOOF%" = config.sane.netns.doof.netnsPubIpv4;
"%ANATIVE%" = nativeAddrs."servo.${flavor}";
"%AOVPNS%" = config.sane.netns.ovpns.netnsPubIpv4;
"%AWAN%" = "$(cat '${dyn-dns.ipPath}')";
"%CNAMENATIVE%" = "servo.${flavor}";
"%ANATIVE%" = nativeAddrs."servo.${flavor}";
"%AOVPNS%" = "185.157.162.178";
};
in
{
wan = {
substitutions = mkSubstitutions "wan";
doof = {
substitutions = mkSubstitutions "doof";
listenAddrsIpv4 = [
nativeAddrs."servo.lan"
bindOvpn
bindDoof
config.sane.netns.doof.hostVethIpv4
config.sane.netns.ovpns.hostVethIpv4
];
};
lan = {
substitutions = mkSubstitutions "lan";
listenAddrsIpv4 = [ nativeAddrs."servo.lan" ];
port = 1053;
};
hn = {
substitutions = mkSubstitutions "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 = {
# # don't need %AWAN% here because we forward to the hn instance.
# listenAddrsIpv4 = [ nativeAddrs."servo.hn" ];
@@ -154,9 +158,10 @@ in
};
sane.services.dyn-dns.restartOnChange = [
"trust-dns-wan.service"
"trust-dns-lan.service"
"trust-dns-doof.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
];
}

View File

@@ -574,10 +574,6 @@ in
gawk.sandbox.wrapperType = "inplace"; # /share/gawk libraries refer to /libexec
gawk.sandbox.autodetectCliPaths = "existingFile";
gdb.sandbox.enable = false; # gdb doesn't sandbox well. i don't know how you could.
# gdb.sandbox.method = "landlock"; # permission denied when trying to attach, even as root
gdb.sandbox.autodetectCliPaths = true;
geoclue2-with-demo-agent = {};
# MS GitHub stores auth token in .config
@@ -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;
driSupport = lib.mkDefault true;
} // (lib.optionalAttrs pkgs.stdenv.isx86_64 {
# for 32 bit applications
# upstream nixpkgs forbids setting driSupport32Bit unless specifically x86_64 (so aarch64 isn't allowed)
driSupport32Bit = lib.mkDefault true;
# upstream nixpkgs forbids setting enable32Bit unless specifically x86_64 (so aarch64 isn't allowed)
enable32Bit = lib.mkDefault true;
}));
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.extraRuntimePaths = [

View File

@@ -66,6 +66,7 @@ end
if vars.percent ~= nil then
bat_args = bat_args .. " --percent-suffix '" .. vars.percent .. "'"
end
bat_args = bat_args .. " {bat}"
-- N.B.: `[[ <text> ]]` is Lua's multiline string literal
conky.text = [[
@@ -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}
${color1}${shadecolor}${font sans-serif:size=22:style=Bold}${alignc}${execp @bat@ ]] .. bat_args .. [[ }${font}
${color1}${shadecolor}${font sans-serif:size=20:style=Bold}${alignc}${texeci 600 @weather@ }${font}
${color1}${shadecolor}${font sans-serif:size=22:style=Bold}${alignc}${execp sane-sysload ]] .. bat_args .. [[ }${font}
${color1}${shadecolor}${font sans-serif:size=20:style=Bold}${alignc}${texeci 600 timeout 20 sane-weather }${font}
${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.extraPaths = [
"/sys/class/power_supply"
"/sys/devices" # needed by sane-sysinfo
"/sys/devices" # needed by sane-sysload
# "/sys/devices/cpu"
# "/sys/devices/system"
];
sandbox.whitelistWayland = true;
suggestedPrograms = [
"sane-sysinfo"
"sane-sysload"
"sane-weather"
];
fs.".config/conky/conky.conf".symlink.target = pkgs.substituteAll {
src = ./conky.conf;
bat = "sane-sysinfo";
weather = "timeout 20 sane-weather";
};
fs.".config/conky/conky.conf".symlink.target = ./conky.conf;
services.conky = {
description = "conky dynamic desktop background";

View File

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

View File

@@ -55,7 +55,7 @@ in
# - theme-demo
# - timeout-completed
# - 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";
parent-theme = "default";
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.br = "branch";
alias.co = "checkout";
alias.com = "commit";
alias.cp = "cherry-pick";
alias.d = "difftool";
alias.dif = "diff"; # common typo

View File

@@ -1,12 +1,12 @@
# gnome feeds RSS viewer
{ config, lib, sane-lib, ... }:
{ config, lib, pkgs, sane-lib, ... }:
let
feeds = sane-lib.feeds;
all-feeds = config.sane.feeds;
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
in {
sane.programs.gnome-feeds.fs.".config/org.gabmus.gfeeds.json".symlink.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,
# with dict["tags"] a list of string tags.
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, ... }:
{
sane.programs."gnome.gnome-maps" = {
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-maps;
suggestedPrograms = [
"geoclue2"
];
sandbox.method = "bwrap";
sandbox.whitelistDri = true; # for perf
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>
# irc #mepo:irc.oftc.net
#
{ config, lib, ... }:
{
sane.programs.mepo = {
sandbox.method = "bwrap";
@@ -16,11 +16,11 @@
{ type = "file"; path = ".cache/mepo/savestate"; }
];
# give mepo access to gpsd for location data, if that's enabled.
# same with geoclue2.
suggestedPrograms = lib.optional config.services.gpsd.enable "gpsd"
++ lib.optional config.services.geoclue2.enable "geoclue2-with-demo-agent"
;
# enable geoclue2 and gpsd for location data.
suggestedPrograms = [
"geoclue2"
# "gpsd" #< not required, and mepo only uses it if geoclue is unavailable
];
};
# 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, ... }:
{
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 = {
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
# - <https://github.com/nwg-piotr/nwg-panel/issues/269>
# - add CPU/meminfo executor
# - use sane-sysinfo
# - use sane-sysload
{
components,
controlsSettingsComponents,
height,
locker,
modulesRight,
playerctlChars,
mediaPrevNext,
windowIcon,
windowTitle,
workspaceHideEmpty,
@@ -49,9 +52,7 @@
modules-center = [
"clock"
];
modules-right = [
"playerctl"
];
modules-right = modulesRight;
clock = {
angle = 0.0;
@@ -79,7 +80,7 @@
battery-low-interval = 4; #< notify every N minutes when battery continues to remain low
battery-low-level = 15; #< notify if battery is lower than this percent
# commands.battery = ""; #< optional action to perform when battery icon is clicked in the drop-down menu
components = components;
components = controlsSettingsComponents;
click-closes = false;
custom-items = [];
css-name = "controls-window";
@@ -90,21 +91,20 @@
menu.icon = "system-shutdown-symbolic";
menu.items = [
{
# TODO: plumb through the configured locker instead of assuming `swaylock`
name = "Lock";
cmd = "swaylock -f -c 000000";
}
{
name = "Logout";
cmd = "swaymsg exit";
cmd = "s6-rc start ${locker}";
}
# {
# name = "Logout";
# cmd = "swaymsg exit";
# }
{
name = "Reboot";
cmd = "systemctl reboot";
cmd = "reboot";
}
{
name = "Shutdown";
cmd = "systemctl -i poweroff";
cmd = "shutdown now";
}
];
menu.name = "Exit";
@@ -127,6 +127,23 @@
interval = 2;
label-css-name = "playerctl-label";
scroll = false;
show-cover = false; #< don't show the little music-note icon
show-previous = mediaPrevNext;
show-next = mediaPrevNext;
show-name = mediaPrevNext;
};
swaync = {
css-name = "swaync-label";
# interval = 1;
# icon-placement = "left";
# icon-size = 18;
# tooltip-text = "";
# on-left-click = "swaync-client -t";
# on-right-click = "";
# on-middle-click = "";
# on-scroll-up = "";
# on-scroll-down = "";
# always-show-icon = true;
};
sway-workspaces = {
angle = 0.0;
@@ -143,6 +160,20 @@
show-name = windowTitle;
};
executor-sysload = {
script = "sane-sysload {mem} {cpu}";
interval = 10;
css-name = "";
on-right-click = "";
icon-size = 16;
show-icon = false;
tooltip-text = "";
on-left-click = "";
on-middle-click = "";
on-scroll-up = "";
on-scroll-down = "";
};
# unused modules:
brightness-slider = {};
dwl-tags = {};

View File

@@ -23,7 +23,8 @@ in
# what looks good:
# - 15px on moby
# - 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 {
type = types.int;
@@ -36,10 +37,18 @@ in
height of the top bar in px.
'';
};
locker = mkOption {
type = types.str;
default = config.sane.programs.swayidle.config.actions.lock.service;
description = ''
s6 service to start which can lock the screen
'';
};
battery = mkEnableOption' true "display battery status";
brightness = mkEnableOption' true "display backlight level and slider";
mediaTitle = mkEnableOption' true "display title of current song/media";
mediaPrevNext = mkEnableOption' true "display prev/next button in media";
sysload = mkEnableOption' true "display system load info (cpu/memory)";
windowIcon = mkEnableOption' true "display icon of active window";
windowTitle = mkEnableOption' true "display title of active window";
workspaceNumbers = mkOption {
@@ -66,8 +75,16 @@ in
# XXX(2024/06/13): wlr-randr does not cross compile
wlr-randr = null; #< only used if not on sway/hyprland; or if using dwl
}).overrideAttrs (base: {
patches = (base.patches or []) ++ lib.optionals (!cfg.config.mediaPrevNext) [
./playerctl-no-prev-next.diff
# patches = (base.patches or []) ++ lib.optionals (!cfg.config.mediaPrevNext) [
# ./playerctl-no-prev-next.diff
# ];
patches = (base.patches or []) ++ [
(pkgs.fetchpatch {
# upstreaming: <https://github.com/nwg-piotr/nwg-panel/pull/309>
url = "https://git.uninsane.org/colin/nwg-panel/commit/a714e4100c409feb02c454874d030d192bfb0ae5.patch";
name = "playerctl: add settings to control which elements are displayed";
hash = "sha256-OofS46wAI3EDE3JbYs/Nn+Vkw9TP1mwSFvk+vBERg2s=";
})
];
# - disable the drop-down chevron by the controls.
@@ -75,17 +92,22 @@ in
# - disable brightness indicator for same reason.
# - *leave* the volume indicator: one *could* remove it, however on desko that would leave the controls pane empty
# making the dropdown inaccessible
# also, remove padding from the items. i can manage that in css and the python padding prevents that.
postPatch = (base.postPatch or "") + ''
substituteInPlace nwg_panel/modules/controls.py --replace-fail \
'self.box.pack_start(box, False, False, 6)' \
'self.box.pack_start(box, False, False, 0)'
substituteInPlace nwg_panel/modules/controls.py --replace-fail \
'box.pack_start(self.pan_image, False, False, 4)' \
'# box.pack_start(self.pan_image, False, False, 4)'
'# box.pack_start(self.pan_image, False, False, 0)'
substituteInPlace nwg_panel/modules/controls.py --replace-fail \
'box.pack_start(self.bri_image, False, False, 4)' \
'# box.pack_start(self.bri_image, False, False, 4)'
'# box.pack_start(self.bri_image, False, False, 0)'
# substituteInPlace nwg_panel/modules/controls.py --replace-fail \
# 'box.pack_start(self.vol_image, False, False, 4)' \
# '# box.pack_start(self.vol_image, False, False, 4)'
substituteInPlace nwg_panel/modules/controls.py --replace-fail \
'box.pack_start(self.vol_image, False, False, 4)' \
'box.pack_start(self.vol_image, False, False, 0)'
'';
# XXX(2024/06/13) the bluetooth stuff doesn't cross compile, so disable it
@@ -102,11 +124,12 @@ in
src = ./style.css;
inherit (cfg.config) fontSize clockFontSize;
};
fs.".config/nwg-panel/common-settings.json".symlink.target = ./common-settings.json;
fs.".config/nwg-panel/config".symlink.target = pkgs.writers.writeJSON "config" (import ./config.nix {
inherit (cfg.config) height windowIcon windowTitle workspaceHideEmpty workspaceNumbers;
inherit (cfg.config) locker height mediaPrevNext windowIcon windowTitle workspaceHideEmpty workspaceNumbers;
# component order matters, mostly for the drop-down.
# default for most tools (e.g. swaync) is brightness control above volume.
components =
controlsSettingsComponents =
lib.optionals cfg.config.brightness [
"brightness"
] ++ [
@@ -116,6 +139,11 @@ in
"battery"
]
;
modulesRight = [
"playerctl"
] ++ lib.optionals cfg.config.sysload [
"executor-sysload"
];
playerctlChars = if cfg.config.mediaTitle then 60 else 0;
});

View File

@@ -1,11 +1,19 @@
commit 7aa759990b38b09abf9010dfe58e4cbdc1493282 (HEAD -> dev-sane)
Author: Colin <colin@uninsane.org>
Date: 2024-06-15 21:41:46 +0000
playerctl: remove backward/forward/music-note icons
these aren't worth the space cost on narrow devices (moby)
diff --git a/nwg_panel/modules/playerctl.py b/nwg_panel/modules/playerctl.py
index 9b53b4b..c4d96ae 100644
index ff48d4c..43ae221 100644
--- a/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:
button_box.set_orientation(Gtk.Orientation.VERTICAL)
- img = Gtk.Image()
- update_image(img, "media-skip-backward-symbolic", self.settings["icon-size"], icons_path=self.icons_path)
- btn = Gtk.Button()
@@ -18,10 +26,10 @@ index 9b53b4b..c4d96ae 100644
self.play_pause_btn = Gtk.Button()
if 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)
button_box.pack_start(self.play_pause_btn, False, False, 1)
- img = Gtk.Image()
- update_image(img, "media-skip-forward-symbolic", self.settings["icon-size"], icons_path=self.icons_path)
- btn = Gtk.Button()
@@ -31,6 +39,21 @@ index 9b53b4b..c4d96ae 100644
- btn.connect("clicked", self.launch, self.PlayerOps.NEXT)
- button_box.pack_start(btn, False, False, 1)
-
self.label = AutoScrollLabel(self.settings["scroll"],
self.settings["chars"],
self.settings["interval"])
self.num_players_lbl = Gtk.Label.new("")
if self.settings["label-css-name"]:
self.num_players_lbl.set_property("name", self.settings["label-css-name"])
@@ -257,13 +239,9 @@ class Playerctl(Gtk.EventBox):
self.box.pack_start(button_box, False, False, 2)
if self.settings["show-cover"]:
self.box.pack_start(self.cover_img, False, False, 0)
- self.box.pack_start(self.num_players_lbl, False, False, 0)
- self.box.pack_start(self.label, False, False, 5)
else:
if self.settings["show-cover"]:
self.box.pack_start(self.cover_img, False, False, 2)
- self.box.pack_start(self.num_players_lbl, False, False, 0)
- self.box.pack_start(self.label, False, False, 2)
self.box.pack_start(button_box, False, False, 10)
def launch(self, button, op):

View File

@@ -20,35 +20,61 @@
font-size: @fontSize@px;
}
button {
margin: 2px;
}
#task-box {
padding-left: 4px;
padding-right: 4px;
}
#task-box-focused {
background-color: @accent-g2;
padding-left: 4px;
padding-right: 4px;
}
#playerctl-button {
background-color: rgba(0, 0, 0, 0.08);
background-image: none;
border: 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;
/* prevent the buttons from pushing the whole bar down */
padding-top: 0px;
padding-bottom: 0px;
margin-top: 0px;
margin-bottom: 0px;
}
#panel-top {
background: @accent-g1;
color: @fg1;
}
/* fix up the top bar sections so that the clock can be centered, even without forcing it to take 1/3rd of the bar */
/* pair with `homogenous = false` in config. on overflow, the clock may be rendered on top of the left portion of the bar */
/* and the right portion of the bar will render on top of all */
#panel-top > box > box > box > #left-box {
margin-left: 0px;
margin-right: -16384px;
}
#panel-top > box > box > box > #center-box {
margin-left: 0px;
margin-right: 0px;
}
#panel-top > box > box > box > box {
/* this is the *parent* of #right-box, which is uniquely under an unnamed "helper box" */
/* i have to address this parent, because otherwise only the controls are visible and the executors (including playerctl) */
/* are packed in a fill mode that pushes them off the visible section of the bar */
margin-left: -16384px;
}
#right-box > widget > * {
/* TODO: tune this for moby */
padding-right: 3px;
padding-left: 2px;
}
#swaync-label {
/* move the notification count closer to the bell icon */
margin-left: -3px;
/* TODO: this should be main font size -1 */
font-size: 14px;
color: @accent-r2;
}
/* increase the size of each workspace icon */
#sway-workspaces-item > label {

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."Apps".symlink.target = ".local/share/applications/rofi-applications.desktop";
fs."WiFi".symlink.target = ".local/share/applications/networkmanager_dmenu.desktop";
persist.byStore.cryptClearOnBoot = [
# this gets us a few things:
# - file browser remembers its last directory

View File

@@ -1,6 +1,6 @@
{ ... }:
{
sane.programs.sane-sysinfo = {
sane.programs.sane-sysload = {
sandbox.method = "bwrap";
sandbox.extraPaths = [
"/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:
# mount it as if it were a directory instead.
"/run/current-system" = "";
} // lib.optionalAttrs config.hardware.opengl.enable {
} // lib.optionalAttrs config.hardware.graphics.enable {
"/run/opengl-driver" = let
gl = config.hardware.opengl;
# from: <repo:nixos/nixpkgs:nixos/modules/hardware/opengl.nix>
gl = config.hardware.graphics;
# from: <repo:nixos/nixpkgs:nixos/modules/hardware/graphics.nix>
package = pkgs.buildEnv {
name = "opengl-drivers";
paths = [ gl.package ] ++ gl.extraPackages;
};
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
gl = config.hardware.opengl;
# from: <repo:nixos/nixpkgs:nixos/modules/hardware/opengl.nix>
gl = config.hardware.graphics;
# from: <repo:nixos/nixpkgs:nixos/modules/hardware/graphics.nix>
package = pkgs.buildEnv {
name = "opengl-drivers-32bit";
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"
# "gnome.gnome-bluetooth" # XXX(2023/05/14): broken
# "gnome.gnome-control-center" # XXX(2023/06/28): depends on webkitgtk4_1
"networkmanager_dmenu"
"nwg-panel"
"pipewire"
"playerctl" # for waybar & particularly to have playerctld running
@@ -163,7 +164,7 @@ in
"swayidle" # enable if you need it
"swaynotificationcenter" # notification daemon
"switchboard" # network/bluetooth/sound control panel
"sysvol" # volume notifier
"syshud" # volume notifier
"unl0kr" # greeter
# "waybar"
"wdisplays" # like xrandr
@@ -198,6 +199,7 @@ in
sandbox.whitelistAudio = true; # it runs playerctl directly
sandbox.whitelistDbus = [ "system" "user" ]; # to e.g. launch apps
sandbox.whitelistDri = true;
sandbox.whitelistS6 = true; #< for Super+L to start the screen locker service
sandbox.whitelistX = true; # sway invokes xwayland itself
sandbox.whitelistWayland = true;
sandbox.extraRuntimePaths = [
@@ -227,7 +229,7 @@ in
'';
fs.".config/sway/config".symlink.target = pkgs.substituteAll {
src = ./sway-config;
src = ./config;
inherit (cfg.config)
extra_lines
font

View File

@@ -32,6 +32,7 @@ in
};
sandbox.method = "bwrap";
sandbox.whitelistS6 = true;
sandbox.isolatePids = false; #< XXX: not sure why, but swaync segfaults under load without this!
};
sane.programs.swaync-fbcli = {
@@ -61,6 +62,8 @@ in
name of entry in /sys/class/backlight which indicates the primary backlight.
'';
};
enableBacklight = mkEnableOption "include a backlight slider in the swaync dropdown (requires an active session with systemd-logind)";
enableMpris = (mkEnableOption "show the currently playing media in the swaync dropdown, and navigation buttons") // { default = true; };
};
};
default = {};
@@ -116,27 +119,14 @@ in
env.GNOTIFICATION_BACKEND = "freedesktop";
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";
positionX = "right";
positionY = "top";
layer = "overlay";
control-center-height = 600;
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-right = 0;
control-center-margin-left = 0;
notification-2fa-action = true;
notification-inline-replies = false;
notification-icon-size = 64;
notification-body-image-height = 100;
notification-body-image-width = 200;
timeout = 30;
timeout-low = 5;
timeout-critical = 0;
fit-to-screen = true; #< have notification center take full vertical screen space
control-center-margin-right = 0;
control-center-margin-top = 0;
# control-center-width:
# pinephone native display is 720 x 1440
# - for compositor scale=2.0 => 360
@@ -144,14 +134,28 @@ in
# - for compositor scale=1.6 => 450
# if it's set to something wider than the screen, then it overflows and items aren't visible.
control-center-width = 360;
control-center-height = 600;
notification-window-width = 360;
keyboard-shortcuts = true;
image-visibility = "when-available";
transition-time = 100;
hide-on-clear = true; #< hide control center when clicking "clear all"
cssPriority = "user"; # "application"|"user". "user" in order to override the system gtk theme.
fit-to-screen = true; #< have notification center take full vertical screen space
hide-on-action = true;
hide-on-clear = true; #< hide control center when clicking "clear all"
image-visibility = "when-available";
keyboard-shortcuts = true;
layer = "overlay";
layer-shell = true;
notification-2fa-action = true;
notification-body-image-height = 100;
notification-body-image-width = 200;
notification-icon-size = 64;
notification-inline-replies = false;
notification-window-width = 360;
positionX = "right";
positionY = "top";
script-fail-notify = true;
timeout = 30;
timeout-critical = 0;
timeout-low = 5;
transition-time = 100;
inherit scripts;
widgets = [
# what to show in the notification center (and in which order).
@@ -164,9 +168,13 @@ in
"dnd"
"inhibitors"
"buttons-grid"
] ++ lib.optionals cfg.config.enableBacklight [
"backlight"
] ++ [
"volume"
] ++ lib.optionals cfg.config.enableMpris [
"mpris"
] ++ [
"notifications"
];
widget-config = {
@@ -228,7 +236,10 @@ in
depends = [ "sound" ]; #< TODO: else it will NEVER see the pulse socket in its sandbox
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";
};
};

View File

@@ -24,7 +24,7 @@
# - SWAYNC_SUMMARY
# 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
fbcli-test-im = {
body = "test:message";

View File

@@ -17,7 +17,12 @@ log() {
checkActive() {
# 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
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() {
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, ... }:
{
sane.programs.switchboard = {
@@ -24,5 +27,9 @@
];
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.whitelistAudio = 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 {
background: transparent;
}
@@ -53,8 +57,8 @@
}
'';
services."sysvol" = {
description = "sysvol: volume monitor/notifier";
services."syshud" = {
description = "syshud: volume monitor/notifier";
depends = [ "sound" ]; #< specifically wireplumber-pulse
partOf = [ "graphical-session" ];
@@ -67,7 +71,7 @@
# -{H,W} N to set the height/width of the notifier, in px.
# -i N to set the size of the volume icon
# -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, ... }:
let
keysForHost = hostName: let
hostCfg = config.sane.hosts.by-name."${hostName}";
in {
"root@${hostName}" = hostCfg.ssh.host_pubkey;
"colin@${hostName}" = 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);
hostKeys = lib.mapAttrsToList
(hostName: hostCfg:
# generate `root@servo`, `colin@servo`, `root@servo-hn`, `colin@servo-hn`, ... as a single attrset:
lib.foldl' (acc: alias: acc // {
"root@${alias}" = hostCfg.ssh.host_pubkey;
"colin@${alias}" = lib.mkIf (hostCfg.ssh.user_pubkey != null && hostCfg.ssh.authorized) hostCfg.ssh.user_pubkey;
})
{}
hostCfg.names
)
config.sane.hosts.by-name;
in
{
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
# args from flake-level `import`
{ hostName }:
{ hostName, variant }:
# module args
{ ... }:
{ lib, ... }:
{
imports = [
@@ -14,4 +14,14 @@
];
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.enable = true;
};
sane.programs.swaynotificationcenter.config = {
enableMpris = false; #< consumes too much screen real-estate
};
sane.programs.waybar.config = {
fontSize = 14;
@@ -63,6 +66,7 @@
windowTitle = false;
mediaPrevNext = false;
mediaTitle = false;
sysload = false;
workspaceNumbers = [ "1" "2" "3" "4" "5" ];
workspaceHideEmpty = false;
};

View File

@@ -80,6 +80,7 @@ in
protocol = [ "udp" ];
visibleTo.lan = true;
visibleTo.wan = cfg.visibleToWan;
visibleTo.doof = cfg.visibleToWan;
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; {
networking.localCommands = let
iptables = "${pkgs.iptables}/bin/iptables";
in-ns = "ip netns exec ${name}";
systemd.services."netns-${name}" = let
ip = lib.getExe' pkgs.iproute2 "ip";
iptables = lib.getExe pkgs.iptables;
in-ns = "${ip} netns exec ${name}";
bridgePort = port: proto: ''
${in-ns} ${iptables} -A PREROUTING -t nat -p ${proto} --dport ${port} -m iprange --dst-range ${netnsPubIpv4} \
-j DNAT --to-destination ${hostVethIpv4}
@@ -41,55 +42,61 @@ let
config.sane.ports.ports
)
;
in ''
ip netns add ${name} || (test -e /run/netns/${name} && echo "${name} already exists")
# DOCS:
# - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/>
# - iptables primer: <https://danielmiessler.com/study/iptables/>
# create veth pair
ip link add ${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 link set ${name}-veth-a up
in {
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:
# - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/>
# - iptables primer: <https://danielmiessler.com/study/iptables/>
# create veth pair
${ip} link add ${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} link set ${name}-veth-a up
# move veth-b into the namespace
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 link set ${name}-veth-b up
# move veth-b into the namespace
${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} link set ${name}-veth-b up
# make it so traffic originating from the host side of the veth
# is sent over the veth no matter its destination.
ip rule add from ${hostVethIpv4} lookup ${name} pref 50 || echo "${name} already has ip rules (pref 50)"
# make it so traffic originating from the host side of the veth
# is sent over the veth no matter its destination.
${ip} rule add from ${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
# 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} || \
echo "${name} already has default route"
# 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 del from all lookup local pref 0 || echo "${name}: already removed ip rule of default lookup (pref 0)"
# for traffic originating at the host veth to the WAN, use the veth as our gateway
# not sure if the metric 1002 matters.
${ip} route add default via ${netnsVethIpv4} dev ${name}-veth-a proto kernel src ${hostVethIpv4} metric 1002 table ${name} || \
echo "${name} already has default route"
# 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 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
# - alternatively, we could fix DNS servers like 1.1.1.1.
${in-ns} ${iptables} -A OUTPUT -t nat -p udp --dport 53 -m iprange --dst-range 127.0.0.53 \
-j DNAT --to-destination ${dns}:53
'' + (lib.concatStringsSep "\n" bridgeStatements);
# in order to access DNS in this netns, we need to route it to the VPN's nameservers
# - alternatively, we could fix DNS servers like 1.1.1.1.
${in-ns} ${iptables} -A OUTPUT -t nat -p udp --dport 53 -m iprange --dst-range 127.0.0.53 \
-j DNAT --to-destination ${dns}:53
'' + (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 = ''
# ${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 ${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" ];
# for some reason network-pre doesn't actually get run before network.target by default??
systemd.targets.network-pre.wantedBy = [ "network.target" ];
systemd.targets.network-pre.before = [ "network.target" ];
# 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.
@@ -114,7 +121,8 @@ in
networking.localCommands = f.networking.localCommands;
networking.iproute2.rttablesExtraConfig = f.networking.iproute2.rttablesExtraConfig;
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);
}

View File

@@ -96,7 +96,13 @@ let
} // extraConfig
);
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;
in {
description = "trust-dns Domain Name Server (serving ${flavor})";

View File

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

View File

@@ -2,17 +2,18 @@
, fetchFromGitHub
, lib
, makeWrapper
, nix-update-script
, pulseaudio
}:
buildGoModule rec {
pname = "blast-ugjka";
version = "0.6.2";
version = "0.7.0";
src = fetchFromGitHub {
owner = "ugjka";
repo = "blast";
rev = "v${version}";
hash = "sha256-Y9Jj+UrrsyRfihHAdC354jb1385xqLIufB0DoikrXYM=";
hash = "sha256-yMwMG0y2ehq2dBMlv9hF+i0TgmMjW3ojBVGiqEUSrhU=";
};
vendorHash = "sha256-yPwLilMiDR1aSeuk8AEmuYPsHPRWqiByGLwgkdI5t+s=";
@@ -26,10 +27,12 @@ buildGoModule rec {
--suffix PATH : ${lib.makeBinPath [ pulseaudio ]}
'';
passthru.updateScript = nix-update-script { };
meta = with lib; {
description = "blast your linux audio to DLNA receivers";
# license = licenses.mit; # MIT + NoAI
inherit (src.meta) homepage;
homepage = "https://github.com/ugjka/blast";
maintainers = with maintainers; [ colinsane ];
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)
# 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.
# 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";
pname = "ether-metamask";
url = "https://github.com/MetaMask/metamask-extension/releases/download/v${version}/metamask-firefox-${version}.zip";
version = "11.16.9";
hash = "sha256-IYRCR0jX2agMHj/Pnnc+q6X92yrhdIJF+uRtw7C2T2k=";
version = "11.16.13";
hash = "sha256-aiWFfGGbjFfKEJSe2wnCfxSABs6ELQ2h0x90IAF3wec=";
};
fx_cast = fetchVersionedAddon rec {
extid = "fx_cast@matt.tf";
@@ -173,8 +173,8 @@ in (lib.makeScope newScope (self: with self; {
extid = "@ublacklist";
pname = "ublacklist";
url = "https://github.com/iorate/ublacklist/releases/download/v${version}/ublacklist-v${version}-firefox.zip";
version = "8.7.1";
hash = "sha256-FvZ2IFlvoAYMmZFXTkGtCZ+44MmXioA271DXvNY96j8=";
version = "8.8.1";
hash = "sha256-b9XiA62Hc32enn04DfUJDVW+Wab5zKgUFwO+oFeTT/w=";
};
ublock-origin = fetchVersionedAddon rec {
extid = "uBlock0@raymondhill.net";

View File

@@ -1,13 +1,14 @@
{ buildLinux
{ lib
, buildLinux
, fetchFromGitLab
#v nixpkgs calls `.override` on the kernel to configure additional things
, features ? {}
, kernelPatches ? []
, kernelPatches ? null
, randstructSeed ? ""
, structuredExtraConfig ? {}
, ...
}:
buildLinux {
buildLinux ({
src = fetchFromGitLab {
owner = "exynos5-mainline";
repo = "linux";
@@ -23,5 +24,9 @@ buildLinux {
autoModules = 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 {
owner = "nix-community";
repo = "nixpkgs-wayland";
rev = "422bb5c5106008418f499ca591c0138f73e213f4";
hash = "sha256-32ukRcPI2W5exf4A7/ISqoKp+sL1MxdMjFt3I+8XS5Y=";
rev = "d0eb8a2eb4b9e30e10b95ed5d99f3501c1b3367d";
hash = "sha256-WA75k8/SJkHd+qaLUJFy4uGM95wDn/18wv+awR0kTbo=";
};
flake = import "${src}/flake.nix";
evaluated = flake.outputs {
@@ -25,7 +25,7 @@ let
in src.overrideAttrs (base: {
# attributes required by update scripts
pname = "nixpkgs-wayland";
version = "0-unstable-2024-06-14";
version = "0-unstable-2024-06-21";
src = src;
# passthru only nixpkgs-wayland's own packages -- not the whole nixpkgs-with-nixpkgs-wayland-as-overlay:

View File

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

View File

@@ -30,6 +30,14 @@ in
# 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' {
title = "unl0kr: 2.0.0 -> 3.2.0";
prUrl = "https://github.com/NixOS/nixpkgs/pull/319126";
@@ -37,9 +45,9 @@ in
})
(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";
hash = "sha256-OX1OP2m9LJYjVcryhpt520XJmdK1XS0KKaEBzMjdpNo=";
hash = "sha256-jVw5pC+f8Z68S/X9EvMB6nbwvoiR03qI7ALeLtkmN24=";
})
(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 @@
{
findutils,
runCommandLocal,
rsync,
}:
runCommandLocal "sane-nix-files" {
nativeBuildInputs = [
findutils
rsync
];
{ stdenv ? null }:
with builtins;
let
src = filterSource
(path: type:
let name = baseNameOf path;
in !(
# mimic .gitignore
(name == ".working")
|| (name == "result")
|| (match "^result-.*" name != null)
))
../../../.
;
meta = {
description = "top-level host configs for Colin's machines";
longDescription = ''
i like to ensure a copy of my config is present on all my machines,
and this does that in a hermetic way.
'';
fakeDeriv = {
# in the bootstrap path, we don't have enough available to actually
# link these files into a derivation.
# but that's ok, because the caller immediately `import`s it anyway,
# 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 ${../../../.}/ ./
chmod u+w .
for pat in $(cat .gitignore); do
set +e
chmod u+w -R "$pat" ; rm -rf "$pat"
find $PWD -name "$pat" -exec 'chmod u+w -R {}; rm -rf {}' \;
set -e
done
rsync -lptr ./ $out/
# alternative implementation which always returns a real derivation,
# but requires a pre-compiled statically-linked `sln` or `cp` implementation.
# self = derivation {
# name = "sane-nix-files";
# system = "x86_64-linux";
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)
# setKeyboard showIfRoom
# ;;
networkmanager_dmenu.desktop)
setKeyboard showIfRoom
;;
org.gnome.Epiphany.desktop)
setKeyboard showIfRoom
;;

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env nix-shell
#!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)
@@ -11,10 +11,24 @@ options:
--hour-suffix <string>: use the provided string as an hours suffix
--icon-suffix <string>: use the provided string as an icon suffix
--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 logging
import time
from dataclasses import dataclass
from enum import Enum
@@ -24,6 +38,7 @@ logger = logging.getLogger(__name__)
# these icons may only render in nerdfonts
ICON_BAT_CHG = ["󰢟", "󱊤", "󱊥", "󰂅"]
ICON_BAT_DIS = ["󰂎", "󱊡", "󱊢", "󱊣"]
ICON_CPU=""
ICON_MEM="☵"
SUFFIX_ICON = "" # thin space
SUFFIX_PERCENT = "%"
@@ -72,6 +87,9 @@ class Formatter:
def render_charge_icon(self, direction: ChargeDirection, percentage: float) -> str:
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:
return f"{ICON_MEM}{self.suffix_icon}"
@@ -137,6 +155,83 @@ class MemInfo:
logger.debug(f"/proc/meminfo: {entry}={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:
"""
reads values from /sys/class/power_supply/$dev/ API
@@ -263,9 +358,39 @@ def try_all_batteries() -> BatteryInfo | None:
@dataclass
class AllInfo:
_fmt: Formatter
_mem: MemInfo | None
_bat: BatteryInfo | None
__bat: BatteryInfo | None = 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
def mem_icon(self) -> str:
if self._mem is None: return ""
@@ -307,6 +432,34 @@ class AllInfo:
else:
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:
logging.basicConfig()
logging.getLogger().setLevel(logging.INFO)
@@ -317,8 +470,7 @@ def main() -> None:
parser.add_argument("--hour-suffix", default=SUFFIX_HR)
parser.add_argument("--minute-suffix", default=SUFFIX_MIN)
parser.add_argument("--percent-suffix", default=SUFFIX_PERCENT)
parser.add_argument("--template", default="{_.bat_icon}{_.bat_time}")
# parser.add_argument("--template", default="{_.mem_icon}{_.mem_pct}")
parser.add_argument("formatstr", nargs="+")
args = parser.parse_args()
if args.debug:
@@ -330,12 +482,19 @@ def main() -> None:
f.suffix_hr = args.hour_suffix
f.suffix_min = args.minute_suffix
info = AllInfo(
f,
MemInfo(),
try_all_batteries(),
)
print(args.template.format(_=info))
info = AllInfo(f)
formatstr = " ".join(args.formatstr)
print(formatstr.format(
bat=LazyFormatter(info, "bat"),
bat_icon=LazyFormatter(info, "bat_icon"),
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__":
main()

View File

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

View File

@@ -107,6 +107,7 @@ let
xdotool
] ++ lib.optionals preferXdgOpen [ xdg-utils ];
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 {
pname = "sxmo-utils";
version = "unstable-2024-02-05";

View File

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

View File

@@ -5,12 +5,12 @@
}:
stdenv.mkDerivation {
pname = "uassets";
version = "0-unstable-2024-06-15";
version = "0-unstable-2024-06-21";
src = fetchFromGitHub {
owner = "uBlockOrigin";
repo = "uAssets";
rev = "bbb9bd592fe54dc2cbc952a68dd715bb481ab83d";
hash = "sha256-r7ZCfHxCNVETu1X7O8vb4xkxTAmYVWFDTHQOZeQ8C2I=";
rev = "05dfb7b5504b20e5432f7092111ad51568b3d9f4";
hash = "sha256-FrSMmZxR+XWUUOb8X1kImEM4Lhw8Ikg8k1vtqXOJD+8=";
};
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-next = nixpkgs.override { variant = "staging-next"; };
nixpkgs-wayland = callPackage ./additional/nixpkgs-wayland { };
opencellid = callPackage ./additional/opencellid { };
peerswap = callPackage ./additional/peerswap { };
phog = callPackage ./additional/phog { };
pipeline = callPackage ./additional/pipeline { };
@@ -84,7 +85,7 @@ let
sane-open = callPackage ./additional/sane-open { };
sane-screenshot = callPackage ./additional/sane-screenshot { };
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 { };
sanebox = callPackage ./additional/sanebox { };
schlock = callPackage ./additional/schlock { };
@@ -97,7 +98,7 @@ let
sxmo_swaylock = callPackage ./additional/sxmo_swaylock { };
sxmo-suspend = callPackage ./additional/sxmo-suspend { };
sxmo-utils = callPackage ./additional/sxmo-utils { };
sysvol = callPackage ./additional/sysvol { };
syshud = callPackage ./additional/syshud { };
tow-boot-pinephone = callPackage ./additional/tow-boot-pinephone { };
tree-sitter-nix-shell = callPackage ./additional/tree-sitter-nix-shell { };
trivial-builders = lib.recurseIntoAttrs (callPackage ./additional/trivial-builders { });
@@ -113,7 +114,6 @@ let
runCommandLocalOverridable
;
unftp = callPackage ./additional/unftp { };
where-am-i = callPackage ./additional/where-am-i { };
zecwallet-light-cli = callPackage ./additional/zecwallet-light-cli { };
# 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; };
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
pythonPackagesExtensions = (unpatched.pythonPackagesExtensions or []) ++ [

View File

@@ -2,6 +2,8 @@
{
depthcharge-tools = callPackage ./depthcharge-tools { };
feedsearch-crawler = callPackage ./feedsearch-crawler { };
fastcluster = callPackage ./fastcluster { };
ols = callPackage ./ols { };
pa-dlna = callPackage ./pa-dlna { };
pyln-bolt7 = callPackage ./pyln-bolt7 { };
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
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>`
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 \
--option restrict-eval true \
--option allow-import-from-derivation true \

View File

@@ -1,5 +1,5 @@
#!/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 "it doesn't check all services, just the most critical ones"
@@ -8,12 +8,40 @@ echo ""
OVPNS_IPV4=185.157.162.178
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
check() {
local label=$1
shift
printf "checking %s\n" "$label"
"$@" > /dev/null
if [ -n "$verbose" ]; then
printf "checking %s (%s) \n" "$label" "$*"
"$@"
else
printf "checking %s\n" "$label"
"$@" > /dev/null
fi
local rc=$?
if [ $rc -ne 0 ]; then
last_error=$rc
@@ -23,6 +51,16 @@ check() {
return $rc
}
runOnHost() {
local host="$1"
shift
if [ "$host" = "$(hostname)" ]; then
"$@"
else
ssh "$host-hn" "$@"
fi
}
check "self-test" false 2> /dev/null
if [ $last_error -eq 0 ]; then
echo "SELF-TEST FAILED" >&2
@@ -31,10 +69,12 @@ if [ $last_error -eq 0 ]; then
fi
last_error=0
parseArgs "$@"
check "uninsane.org. DNS" nslookup uninsane.org.
check "uninsane.org. DNS via external resolver" nslookup uninsane.org. 1.1.1.1
check "uninsane.org. bootstrap DNS" nslookup uninsane.org. ovpns.uninsane.org
check "[1.1.1.1] uninsane.org. DNS" nslookup uninsane.org. 1.1.1.1
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://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 "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 "systemctl --failed:"
ssh uninsane.org systemctl -q --failed
runOnHost servo systemctl -q --failed
echo ""
if [ $last_error -eq 0 ]; then

View File

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

View File

@@ -1,6 +1,8 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p nix-update
NIX_FILES_TOP=/home/colin/nixos
usage() {
echo "update: update rev/hash for one or more packages"
echo "usage: update [options] [attr-path]"
@@ -44,34 +46,51 @@ getPkgs() {
attrPrefix=sane
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)
}
updateOnePkg() {
local attrPath="$1"
if [[ "$attrPath" =~ ^"$ignore" ]]; then
if [ -n "$ignore" ] && [[ "$attrPath" =~ ^"$ignore" ]]; then
warn "ignoring $attrPath"
return
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
warn "don't know how to update '$attrPath'"
return
fi
# make sure everything needed to invoke the script actually exists on disk
nix-build -A "$attrPath.passthru.updateScript" || true
# make sure everything needed to invoke the update script exists in-store
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_PNAME="$(nix eval --raw -f . $attrPath.pname)"
local UPDATE_NIX_OLD_VERSION="$(nix eval --raw -f . $attrPath.version)"
local UPDATE_NIX_NAME=$(nix eval --raw -f "$NIX_FILES_TOP" $attrPath.name)
local UPDATE_NIX_PNAME=$(nix eval --raw -f "$NIX_FILES_TOP" $attrPath.pname)
local UPDATE_NIX_OLD_VERSION=$(nix eval --raw -f "$NIX_FILES_TOP" $attrPath.version)
info "updating: '$attrPath'"
debug "$updateScript"
# 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 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.
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=

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": {
"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"
}
],
"lastmodified": "2023-10-28T10:34:31Z",
"mac": "ENC[AES256_GCM,data:GS492dizmvoW6uCO4Wz+YzwFbtM6LfUdEgx7DzSbHG3hM/q3k5LW8E4HzxkBkDUylL5GS3FUWCADcoKe+JWqX3TUqQDqqrYScAVcE5e9TM2mZ12U6xz1SU8Tc5DkqQuUgrv/pvaJBI1UlEEdFkSOSIBzHZ5C7xW0WsNFauC9HFI=,iv:rnDBUWzieefIpES4WjWWEiAY3LtKkfazAgHaSoJ9VEw=,tag:n4b6kctXHHKiQfQAeQeFTw==,type:str]",
"lastmodified": "2024-06-23T17:59:53Z",
"mac": "ENC[AES256_GCM,data:ZKGwqisn27YnSjroCxrCxIoR5vkYgnfzVoLNbxfnti+RFUX67eBfcj0jOc8KqfAWyNOCMlWlfOZ1AonIOqsBqhPrX1LEyOdhrCp+M7zfhnIheOJFfqVyUzN0O7N0PIj696tM8HgYg2OX+YLCvZvFf9DSrK92RsRCkWSfH34OKd8=,iv:AByoesqgTOoet2BR01d6G1CBx0o/PDGj2kbxNU5St20=,tag:Um2vz1mdD5gu4CifY4QuTA==,type:str]",
"pgp": null,
"unencrypted_suffix": "_unencrypted",
"version": "3.8.1"

View File

@@ -0,0 +1,52 @@
{
"data": "ENC[AES256_GCM,data:gQxh1msPLuraF/GEczHeheDUvWZcvVyKZDY/mD85LJHhjnnqPWGOrIvZjFaH1bVg71VAgGSIdC66UnoptaKxA6j6BE0aB6SOsvLQOxXdvEELTECT0Gy17wLXNM7tVFUKIFreLM3gykZXYnT44IzkjLDduIt5TfvgvE/Jr2gLQpPq0ooVgcExuueSTGHYxkKEvHb+TwaLSbg/ndWiq1W7i0ZaGt4FIOt/9gHqMcOBfpKp/IqyVidjG4aXLLcb96gsOqS/sv+acVr9rM3ZfEWyWbwWoNTiKn3AnFU/l6Cj/TmGcDa8M+WfwWTpy8DqEzQ6CH2jBtuYs5w+O7ccAZREE2+x5OE8S7NhEluoO+DSmHZfk1tywLtSlS6cWmrhdbSc2xqKxPDQln+y5gjYHD95ccQrdK1TbSgkYu2AH/Jpjcmm/3z6v8NJDtL8VRQh9Q4S20QMTN8D3B14AGKXc8ulHDnw7wJpN6QF38b51vqGK8Jjc3hlPnRnS2pBOxVfa5BZFCgBaVfxqpRSBA85wQQzpjdHG/lQ8uwtDHR4fGsafPFOOhzhA+aUKTIZ/oCEnf9X8jJ6O/j5k0NO/QUU5H36/V9Hh+bywyacT9fgqPuhu68R/eb/aNaaY6bebYiaIaxHulpzAyobAbC9ls3VMYk7S4TxSRnhLp1kxpmiO32n5eCiW/tEm8Xxa4N7KXwfyWccwHjm0Y5jOyKhXtK60lv8/vR2zesOPiyOh57fCx8V5R/GO1RpY3Wd94gBsXULGOb+H4DD+fuSCWAr4iajD8ZGoFj6p2yFxP5RBUDffO08ahF6HCe29sqAf4DoVGFFvlulj1JB0436zy44y+/tOf0+ztOuWP4ERzpfgVY8pWUgOQpwKJ3bEoojUokqhHUvXG1RQzKohOcInn4EUmi9L7aLVfijw6IwpJXAXUfbnuacBalAhrpQhmU7THs94HSXZ56yUk0ukbmNxoHoaG378RApoPJVXKQL68IYfV05+rr7i+w096TiSFbvSWC1/6cULwVNQMQdOasiiJVkNeM5D9ecbB+QIr8edcRX5vvhtwRiqoqNOJNwlKGshLK+gJJQRikD+IrQpWcrHWZBICF+Vax4GAxNk1jfOtfDzDE7RkOi8v3aZLBmxszi8MIlAbvjQijr52iFiBLoSTCftQ+uOXVt1aNkOe2JDX1gHzZdUJIZyssgwPFB12ggWRB8VxQHY6K8T34J3M77GvELjpyx07L4elQpg/WqhX/qZXNHjdk3AdbCUYWYqvlMXa7JXq1HTHMpnPsq0sNyqpUARD3OuQB9bDrfsrOYkgTU8J8OOfbdr+bsCYINDDVb4unior256z9h7f+mY28YiJeHJBg0YgCHLK5rdZ23qIbxDiGkYuLPLVAhAVXdbjgcNYTldKBzjq9GPdqoXWvtWZgCewzxf2FPMrPMqw7A+vnn2exZt0v/F9xGFL/885I0Tnbq7UpTesvJ4dqj8qIkUjmpVXUcbuEPDMMIt3o+5Tf9JquhAL1V1DscY77N3SoHe+8NdZLP4hgP7/t7LB757BVf86ZUsDvIpfprjNUoJ7wwnmK6yMtHH2026wrRx25eHNFqbLVVzVeg28grXWEV3xU1y0S5qvheAp1aotFTHP9lrS/LGlpIqnNnDpod9ecdkk/VEeib1+QG19SEBYcVYjmGvLpYNg+EZS8w1Ca+P1kvNXRUojeyUKk5aVCyo10f/PE/kKaFmF42+BZxaxf6GY+TwTSvDlsqfDnzn8bse/fTmjLsYNlXEl4Xgb+idQfrGTUaP+t13zAFsqDKGap7F25xKyjY+feZo9iNosgVAaaplP0CHFpEYAxnTtC+YIZ78Yjo3H2dVWVo9o3JAPKlsSEccjgy5rtSXqhmHK0GFEtnNDj8teOFJ+y/EwxEMiFK7XAZpnvdSEuEZeIJH10hWb0Yo52hZrotx11Xy0t3C9LqiW4kjzpLlMpfgFJNtsd/+9L5RwIClvJnmrVcpe9xCvDQ5c4/3Wk/zykx3qIxgNUXVkDjqbKGcSJJOorwPDquPmDSmzu70qaMn0FdCdlWzq2iwdsGcrp9PSH/C52IvM9XooNPrrq/Wt/grC/lhhUrhGAS6G6tXgmJe767IdaMCFysHgn4hlXWQ3KzJaIJC1cJoFPZgczcGBdqnsa3gxXOqOc3vBcABB2lNpPp2wIqHkvZZBzTHIgkNSdW74QzrsNn2yF3aKP+wTHuaeDeVGWW5VFwzwGJGY/50vlel5JWP7TtBnHQ2gU6QUnswF/Cv6D92kdh92GqSC1pzzQ/weq3+4XCN+QpDqLf7jZfR7YUv2iJup3cpfo/++ixTD4uPSuYf4zsyAec4HgV2IXQwCm32pST4eQfFPa2j3wE33K05mqMCnNL9CqbXf1emImnZLPlDeZuDQ+CuuZ7iC5EpO6+bgjnIuZypVlVaVp0axNNfyUmL25kTdz1EdXXLkMKLUJld6EyFnnwJPfcBBBtmkL5j7Slxjdp/nAW3zr8Buk2zzCMnJzkVkgpSet4IbBLbz1+f0MBG5JKVGoNyeeFrIC/ZDcjHQd6LMbss9QVhPhANl9Msj4WS/Qfe4TI8He8Qy9izIRltakF78uLfI3zcjk+6ZIC42vc9N3MKjLHJ0/W5S3s4wEAtgIRGf33b7wtShE8st5vYGOiwTY/IGfpsySGKJwaEvQssRRjUWlOCmOCJthrd9zK2Xh40lGAgkUnPI/36orVF+hV1jLHHuyznVMwfXb1syXLdIYevoUJPCxs7Jnkj2re5G2v/7YXFlvMLqewNrBg,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"
}
}