1
0
forked from colin/nix-files

Compare commits

...

186 Commits

Author SHA1 Message Date
80e6d9743f nixpkgs: 2025-09-17 -> 2025-09-18 2025-09-18 17:23:44 +00:00
180829b4da nixpkgs-wayland: bump 2025-09-18 17:23:35 +00:00
8eb332140e uassets: 2025-09-17 -> 2025-09-18 2025-09-18 17:23:21 +00:00
e551ab0eba nixpkgs: 2025-09-14 -> 2025-09-17 2025-09-17 17:19:20 +00:00
52b58b1f63 nixpkgs-wayland: 2025-09-15 -> 2025-09-17 2025-09-17 17:19:06 +00:00
3306f4dda5 sops-nix: 2025-09-14 -> 2025-09-16 2025-09-17 17:15:09 +00:00
7e14b86ded uassets: 2025-09-15 -> 2025-09-17 2025-09-17 17:14:51 +00:00
f31067a18a nixpkgs-bootstrap-updater: fix breakage from earlier nix-shell --help installCheck change 2025-09-15 20:30:00 +00:00
a769ab6af3 nixpkgs-wayland: 2025-09-14 -> 2025-09-15 2025-09-15 18:28:49 +00:00
4ce58798b5 mslicer: 2025-07-18 -> 2025-09-14 2025-09-15 18:28:36 +00:00
b8ce3e570f sops-nix: 2025-09-10 -> 2025-09-14 2025-09-15 18:28:21 +00:00
daa3d68f29 uassets: 2025-09-14 -> 2025-09-15 2025-09-15 18:28:08 +00:00
4951db607c fix mimeo & sane-die-with-parent, fallout from static-nix-shell changes 2025-09-15 18:27:22 +00:00
f7b6981135 servo: dyn-dns: persist wan.txt across reboots 2025-09-15 04:48:45 +00:00
3fe684f2c1 dyn-dns: move files from /var/lib/uninsane -> /var/lib/dyn-dns 2025-09-15 04:46:40 +00:00
039c561ae6 static-nix-shell: spot-check binaries by invoking them with --help
this actually did find one error, in a python script that was trying
to use __doc__ as the help message (incorrectly)
2025-09-15 01:55:51 +00:00
619de95e58 nixpkgs: 2025-09-13 -> 2025-09-14 2025-09-14 19:19:12 +00:00
4228a939c5 nixpkgs-wayland: 2025-09-13 -> 2025-09-14 2025-09-14 19:18:41 +00:00
04ce5789d5 wikipedia_en_100: 2025-08 -> 2025-09 2025-09-14 19:18:23 +00:00
14d48255a5 uassets: 2025-09-13 -> 2025-09-14 2025-09-14 19:18:10 +00:00
26dc346cb3 feeds: subscribe to The Final Straw Radio (podcast) 2025-09-14 08:08:24 +00:00
46f11b3d7e knot-dns: push cross fix upstream 2025-09-14 07:26:05 +00:00
cdad23d7c1 overlays: remove upstreamed delfin patch 2025-09-14 07:16:30 +00:00
71bd25063f overlays: re-enable doCheck for tailscale
tests pass now
2025-09-14 07:16:12 +00:00
adf23c093c nixpkgs: 2025-09-12 -> 2025-09-13 2025-09-13 19:29:58 +00:00
232f0a7958 nixpkgs-wayland: 2025-09-11 -> 2025-09-13 2025-09-13 19:29:39 +00:00
9fad26b20d uassets: 2025-09-12 -> 2025-09-13 2025-09-13 19:29:20 +00:00
99bdac91c3 nixpkgs: bump and drop upstreamed rofi cross patch 2025-09-12 18:23:47 +00:00
d80832b3c8 nixpkgs: 2025-09-11 -> 2025-09-12 2025-09-12 16:17:46 +00:00
13b1c81090 nixpkgs-wayland: bump 2025-09-12 16:17:29 +00:00
1413fac58c uassets: 2025-09-11 -> 2025-09-12 2025-09-12 16:17:07 +00:00
278d01dd8a bind: remove the extraArgs patch, since i'm not using bind anymore
will need to be re-added if i switch back to bind,
although it looks unlikely that this patch will be upstreamed
so maybe best to take a different approach if i need it again.
2025-09-12 06:19:08 +00:00
0eff2ff545 signal-desktop: update cross patch hash 2025-09-12 04:45:55 +00:00
8bc7afede6 cross: push rofi patch upstream 2025-09-12 04:45:08 +00:00
fb49022b14 programs: ship nvimpager as a user program 2025-09-12 01:39:20 +00:00
bd0f6e9cf6 /mnt/desko/home: mount over wireguard so it's available outside the LAN 2025-09-11 22:02:23 +00:00
3b5a795535 nixpkgs: 2025-09-10 -> 2025-09-11 2025-09-11 16:15:26 +00:00
1b48de661a nixpkgs-wayland: 2025-09-10 -> 2025-09-11 2025-09-11 16:15:10 +00:00
f492afa35a sops-nix: assets-unstable-2025-09-09 -> assets-unstable-2025-09-10 2025-09-11 16:14:50 +00:00
75f27f1496 uassets: 2025-09-10 -> 2025-09-11 2025-09-11 16:14:34 +00:00
598bf2e5be nixpkgs: unstable-2025-09-09 -> unstable-2025-09-10 2025-09-10 19:41:05 +00:00
474f1a9e3c nixpkgs-wayland: 0-unstable-2025-09-07 -> 0-unstable-2025-09-10 2025-09-10 19:40:39 +00:00
39002c3700 sops-nix: assets-unstable-2025-08-12 -> assets-unstable-2025-09-09 2025-09-10 19:40:19 +00:00
c5ccbf1405 uassets: 2025-09-09 -> 2025-09-10 2025-09-10 19:40:03 +00:00
2f0a2cc27b alacritty: disable the annoying Ctrl+Shift+Space shortcut 2025-09-09 22:54:26 +00:00
c5dc5a7561 nvimpager: inherit nvim config & be the default PAGER 2025-09-09 22:33:03 +00:00
2def3204d0 neovim: don't unnecessarily set GIT_EDITOR 2025-09-09 22:06:27 +00:00
3d37f743e7 git: tune defaults 2025-09-09 22:05:32 +00:00
6acd76fbb5 nixpkgs: 2025-09-08 -> 2025-09-09 2025-09-09 16:26:13 +00:00
d9f4934d97 uassets: 2025-09-08 -> 2025-09-09 2025-09-09 16:26:03 +00:00
18a045c000 linuxPackages.perf -> perf (upstream nixpkgs name change) 2025-09-09 05:36:41 +00:00
eb4b4b31cf sops: fix vim editor 2025-09-09 05:19:43 +00:00
5d2a73afb5 dns: switch bind -> kresd (a.k.a. knot-resolver) 2025-09-09 05:19:25 +00:00
f2d5bfcf6b nixpkgs: 2025-09-06 -> 2025-09-08 2025-09-08 17:06:10 +00:00
dd4131ba16 nixpkgs-wayland: 2025-09-05 -> 2025-09-07 2025-09-08 17:05:50 +00:00
9b1fbebc3c uassets: 2025-09-06 -> 2025-09-08 2025-09-08 17:05:30 +00:00
9cbe47cee0 transmission: document transmission_3 package 2025-09-08 07:55:13 +00:00
f95d5dd501 scripts/sync: remove lappy 2025-09-08 07:53:27 +00:00
49250d9901 scripts/sync: document pkm/music/books/photos flags 2025-09-08 07:52:25 +00:00
76744c427a nixpkgs: 2025-09-04 -> 2025-09-06 2025-09-06 22:30:17 +00:00
91e821c027 cross: fix knot-resolver cross compilation
not used yet, but in preparation for switching BIND -> kres
2025-09-06 22:30:08 +00:00
72c354ca2c nixpkgs-wayland: 2025-09-03 -> 2025-09-05 2025-09-06 22:29:31 +00:00
c24f855502 uassets: 2025-09-04 -> 2025-09-06 2025-09-06 22:29:17 +00:00
4cfb374a5d nixpkgs: remove upstreamed gmobile cross patch 2025-09-05 03:43:33 +00:00
e422552633 nixpkgs: 2025-08-31 -> 2025-09-04 2025-09-05 03:41:52 +00:00
a4a1a5afce nixpkgs-wayland: 0-unstable-2025-08-31 -> 0-unstable-2025-09-03 2025-09-05 03:41:41 +00:00
3826a103df euicc-manual: 2025-08-30 -> 2025-09-02 2025-09-05 03:41:27 +00:00
a12593b608 uassets: 2025-08-31 -> 2025-09-04 2025-09-05 03:41:04 +00:00
815c16a6d1 moby: switch to systemd-boot 2025-09-03 23:48:28 +00:00
b46a294e56 pine64-alsa-ucm: hack in support for EFI/systemd-boot 2025-09-03 23:46:44 +00:00
8a90311ea6 firefox: enable desktop-notification permissions 2025-09-03 23:43:52 +00:00
7d18ebd6da rk3399-pinephone-pro-sound: document simple-audio-card,routing property 2025-09-03 07:53:16 +00:00
80a528b790 moby: limit extlinux configs to 5 2025-09-03 07:52:40 +00:00
212e81d521 pinephone-pro-sound-minimal: remove extraneous i2c timings 2025-09-02 17:06:42 +00:00
6c137994e9 pinephone-pro-sound-minimal: remove unused in2-differential config; redundant #address/#size-cells 2025-09-02 16:42:58 +00:00
7a5df886a1 pinephone-pro-sound-minimal: remove Internal Earpiece, Headset Microphone routes
the Internal Microphone (i.e. not the headphones one) still works (did HP mic *ever* work?)
2025-09-02 16:37:39 +00:00
6393ec68da pinephone-pro-sound-minimal: remove Line In/Out Modem controls 2025-09-02 16:29:23 +00:00
1c4cd0aad5 pinephone-pro-sound-minimal: remove i2s0_2ch_bus override 2025-09-02 16:23:51 +00:00
acbbed7149 pinephone-pro-sound-minimal: remove the uart pinctrl
this means we get distortion at (very) high output levels again, but a simpler profile
2025-09-02 16:09:08 +00:00
71a515f235 pinephone-pro-sound-minimal: remove the unused hp-det pinctrl 2025-09-02 16:02:56 +00:00
7c14f46a4f pinephone-pro-sound-minimal: disable card-level hp-det-gpios
note that the 'Speakers' profile doesn't give sound when headphones actually are plugged in. the 'Headphones' profile does, of course
2025-09-02 15:54:41 +00:00
10cabf5056 moby: switch systemd-boot -> extlinux, until i figure out the audio issues 2025-09-02 09:20:05 +00:00
47ac5ca23c pinephone-pro: define an alternate, more minimal sound dtso
and yes, i actually tested this before committing this time
2025-09-02 09:04:55 +00:00
52b5626903 pinephone-pro-sound: add missing VCC-supplynode to speaker_amp 2025-09-02 04:02:40 +00:00
f645005b45 pinephone-pro: ship a binary helper to install (or update) u-boot on an existing (or new) device
moby is now updated to u-boot 2025.07
2025-09-01 22:25:48 +00:00
9979289be1 u-boot-pinephone-pro: refactor (build output is unchanged) 2025-09-01 20:21:07 +00:00
a1396fb273 move bootpart-systemd-boot installation into modules/image, so it reliably applies to hosts which need it 2025-09-01 20:20:43 +00:00
1c38797bad modules/image: fix extraGPTPadding; fix pinephone and pinephone-pro image installation 2025-09-01 20:19:46 +00:00
c66764fba5 hal/pine64-pinephone-pro: fix image build to not be truncated 2025-09-01 11:24:05 +00:00
7e7cac8ecd fix arm-trusted-firmware eval after apparent upstream refactoring to buildArmTrustedFirmware 2025-09-01 11:23:18 +00:00
e7a1ff8b3d rpi-400: note linux_rpi4 2025-09-01 09:51:59 +00:00
e43926eb25 nixpkgs: patches: remove commented-out, irrelevant rpm patch 2025-09-01 09:51:04 +00:00
4dbce1baae cross: update upstreaming status 2025-08-31 23:35:01 +00:00
55979b7e4e aarch64: enable vulkan-tools (it cross compiles) 2025-08-31 22:44:16 +00:00
fadcc1a842 hal/aarch64: only patch SUNXI_CCU=y on aarch64 PLUS preferBuiltin=false
maybe better to gate it on autoModules too?
2025-08-31 22:35:33 +00:00
5526bba05c nixpkgs-bootstrap: 2025-08-30 -> 2025-08-31 2025-08-31 22:07:43 +00:00
26412ee801 nixpkgs-wayland: 2025-08-30 -> 2025-08-31 2025-08-31 22:07:10 +00:00
3cdbd133f6 uassets: 2025-08-30 -> 2025-08-31 2025-08-31 22:06:44 +00:00
bd8c7c971a aarch64: kernel: remove forced BACKLIGHT_CLASS_DEVICE=y (fixed upstream)
fixed in nixpkgs 2024-12-27: <https://github.com/NixOS/nixpkgs/pull/367774>
2025-08-31 13:42:28 +00:00
4c4a24d08d bootpart-edk2-rpi: ship the rpi-400 dtb 2025-08-31 13:19:27 +00:00
86b037c835 modules/image: ship devicetree file, if configured 2025-08-31 12:57:15 +00:00
32ea5ad846 boot: re-enable nixpkgs default kernel modules
this doesn't fix cadey boot completely, but it gets it to fewer errors
2025-08-31 12:26:13 +00:00
3a1eec4308 edk2-rpi: swap raspberry logo with tianocore logo, to remove dependency on edk2-non-osi repo 2025-08-31 11:19:39 +00:00
e81a2498b7 bootpart-edk2-rpi: minimize custom config portions 2025-08-31 10:39:41 +00:00
5f8cfcb7e9 edk2-rpi: fix!
now it actually loads. havent tried booting the full systemd-boot enabled system though
2025-08-31 09:47:26 +00:00
13205492b4 bootpart-edk2-rpi: dont ship .dtb files 2025-08-31 09:04:18 +00:00
176ef307b2 rpi-400: try to boot via EDK2
however, my build of edk2 seems broken. drop the prebuilt RPI_EFI.fd into the resulting image, and it works ...
2025-08-31 08:58:43 +00:00
11293a4d0a bootpart-edk2-rpi: init
have not deployed this yet; untested
2025-08-31 08:20:17 +00:00
a34449faed nixpkgs-bootstrap: 2025-08-29 -> 2025-08-30 2025-08-30 21:12:42 +00:00
4f3b1dd361 nixpkgs-wayland: 2025-08-29 -> 2025-08-30 2025-08-30 21:10:15 +00:00
f7335634a1 euicc-manual: 2025-08-14 -> 2025-08-30 2025-08-30 21:10:01 +00:00
faa8a6968d uassets: 2025-08-29 -> 2025-08-30 2025-08-30 21:09:45 +00:00
fd54b70b36 edk2-rpi4: init 2025-08-30 05:16:36 +00:00
05b864143e todo.md: remove outdated comment about envelope/libadwaita 2025-08-30 01:32:18 +00:00
d9b82e8b1e moby: plumb deviceTree file through to systemd-boot
this fixes the lacking battery charge indication from the previous extlinux -> systemd-boot change
2025-08-30 01:22:52 +00:00
ad559acbd9 moby: switch extlinux -> systemd-boot
for the sake of consistency with my other platforms

u-boot boot order appears to be extlinux > script > efi_mgr > efi

note that after rebooting, battery shows 0%. unclear if systemd-boot somehow breaks charging, or just battery reporting, or if totally unrelated
2025-08-30 00:30:19 +00:00
4d4b28027c todo.md: remove outdated servo /boot size item 2025-08-29 20:56:19 +00:00
da52f21da2 remove commented-out generic-extlinux-compatible configs 2025-08-29 20:55:41 +00:00
fea85f438b servo: fix prosody
well, i know it works with the systemd hardening disabled. i'm assuming it'll work with that enabled too, but don't want to redeploy/restart the service right now
2025-08-29 19:46:42 +00:00
13db8bec76 nixpkgs: 2025-08-28 -> 2025-08-29 2025-08-29 16:17:17 +00:00
01e3ace398 nixpkgs-wayland: 2025-08-28 -> 2025-08-29 2025-08-29 16:17:03 +00:00
745ee33394 uassets: 2025-08-28 -> 2025-08-29 2025-08-29 16:16:49 +00:00
1c5c9b80eb nixpkgs: 2025-08-27 -> 2025-08-28; remove upstreamed newsflash cross code 2025-08-28 16:01:56 +00:00
94289c2253 nixpkgs-wayland: 2025-08-27 -> 2025-08-28 2025-08-28 16:00:27 +00:00
0c443fae25 uassets: 2025-08-26 -> 2025-08-28 2025-08-28 16:00:14 +00:00
a490a74390 cross: open PRs for all my rust cross patches 2025-08-28 01:57:17 +00:00
2e71e06c05 lemoa: inline the cross compilation fix 2025-08-27 18:43:49 +00:00
203832b5a8 envelope: 2024-09-13 -> 2025-05-17; inline the cross fixes 2025-08-27 18:37:55 +00:00
1204f4db69 cross: rewrite newsflash patch as a nixpkgs commit 2025-08-27 17:04:05 +00:00
a4b114fce2 nixpkgs: bump; rewrite snapshot patch as nixpkgs commit 2025-08-27 16:29:58 +00:00
7e17eb4056 nixpkgs-wayland: 2025-08-26 -> 2025-08-27 2025-08-27 15:52:50 +00:00
259d980a60 nixpkgs: 2025-08-26 0> 2025-08-27 2025-08-27 15:52:40 +00:00
5488486944 firefox: fix browserpass/native-messaging-hosts integration 2025-08-27 08:32:35 +00:00
969717b1fe firefox: disable safebrowsing and restrict app auto-updates even more aggressively
i don't think any auto-updating bit me, i'm just being pre-emptive
2025-08-27 07:55:45 +00:00
7391e34f77 cross: factor spot,video-trimmer build fixes into nixpkgs commits 2025-08-27 02:25:28 +00:00
7f45077485 cross: split delfin fix into nixpkgs patch 2025-08-27 01:12:25 +00:00
ceb7ccbc6d todo.md: task for migrating spot -> riff 2025-08-27 01:06:14 +00:00
9d63ec5dd2 hosts: remove references to lappy remote fs
this was causing mount timeouts on _every_ deploy
2025-08-27 00:00:21 +00:00
7ce93eae96 cross: fix papers via upstreamable patch 2025-08-26 23:52:58 +00:00
1277c73304 nixpkgs: update, drop upstreamed loupe/pwvucontrol cross patches 2025-08-26 18:14:55 +00:00
0550498cd1 nixpkgs/patches: remove redundant qemu patch, push coincurve upstream 2025-08-26 17:02:45 +00:00
023396a41e nixpkgs-bootstrap.staging: 2025-08-25 -> 2025-08-26 2025-08-26 15:44:27 +00:00
ebb335ef4c nixpkgs-wayland: 2025-08-25 -> 2025-08-26 2025-08-26 15:44:27 +00:00
fbb0046dda uvtools: 5.1.7 -> 5.2.0 2025-08-26 15:44:27 +00:00
59d4197bf5 uassets: 2025-08-25 -> 2025-08-26 2025-08-26 15:44:27 +00:00
90e4e20274 zimPackages.wikipedia_en_all_maxi: 2024-01 -> 2025-08 2025-08-26 15:44:27 +00:00
7ecd368e20 zimPackages.alpinelinux: 2025-07 -> 2025-08 2025-08-26 15:44:27 +00:00
79fc30da0e cross: push fractal/pwvucontrol/loupe patches upstream & update tracking statuses 2025-08-26 15:44:27 +00:00
23f3647cc5 nixpkgs-wayland: 2025-08-20 -> 2025-08-25 2025-08-26 15:44:27 +00:00
ad4910366d zimPackages.archlinux_en_all_maxi: 2025-07 -> 2025-08 2025-08-26 15:44:27 +00:00
609becadfe nixpkgs: 2025-08-18 -> 2025-08-26 2025-08-26 15:44:27 +00:00
3acabe60b6 uassets: 2025-08-20 -> 2025-08-25 2025-08-26 15:44:27 +00:00
87ec095b8a programs: typescript-language-server: link cache files into ephemeral storage 2025-08-26 15:44:27 +00:00
8831d8d1ac image: fix initrd path to be the /boot path instead of the /nix/store path 2025-08-22 02:58:09 +00:00
8b333a8887 doc/migrating-storage-device: show how to resize the fs 2025-08-22 02:58:09 +00:00
028d903e9c boot: package "mlabel", for changing FAT UUIDs 2025-08-22 02:20:05 +00:00
dfed5f070b servo: update fs UUIDs 2025-08-22 02:19:47 +00:00
b29ee5ac03 desko: gate ollama behind sane.maxBuildCost option 2025-08-21 02:43:35 +00:00
e700ff392f servo: gate costly services behind sane.maxBuildCost option 2025-08-21 02:42:58 +00:00
91578c0b78 snippets: add new links 2025-08-20 17:31:19 +00:00
b35656c9ae nixpkgs-wayland: 2025-08-19 -> 2025-08-20 2025-08-20 17:24:48 +00:00
726281a6dd uassets: 2025-08-19 -> 2025-08-20 2025-08-20 17:24:09 +00:00
f305027678 nvimpager: allow access to vimrc 2025-08-20 01:17:37 +00:00
2b69c07d12 nixpkgs-wayland: 2025-08-18 -> 2025-08-19 2025-08-19 08:35:30 +00:00
544b1e58e0 uassets: 2025-08-18 -> 2025-08-19 2025-08-19 08:35:18 +00:00
34c2d4f66f neovim: ship RC file as ~/.config/nvim/
this allows easier editing at runtime
2025-08-18 21:22:21 +00:00
4addf857b7 firefox: redirect "maps" search URL to Kagi by default 2025-08-18 19:39:31 +00:00
a3f6c148d3 nixpkgs: 2025-08-17 -> 2025-08-18 2025-08-18 15:25:24 +00:00
43a0abd68f nixpkgs-wayland: 2025-08-17 -> 2025-08-18 2025-08-18 15:25:15 +00:00
b3c4e96d6e syshud: 2025-07-26 -> 2025-08-18 2025-08-18 15:24:56 +00:00
ade5ce5339 uassets: 2025-08-17 -> 2025-08-18 2025-08-18 15:24:40 +00:00
e543034fcb overlays/cross: update upstreaming status 2025-08-17 21:25:15 +00:00
b5d96ed17b nixpkgs: 2026-08-16 -> 2025-08-17 2025-08-17 17:32:26 +00:00
003ce70cd7 nixpkgs-wayland: 2025-08-15 -> 2025-08-17 2025-08-17 17:32:16 +00:00
04f6964711 uassets: 2025-08-16 -> 2025-08-17 2025-08-17 17:31:59 +00:00
63cf19f839 nixpkgs: 2025-08-15 -> 2025-08-16 2025-08-16 21:05:35 +00:00
806a1aa294 nixpkgs-wayland: 2025-08-14 -> 2025-08-15 2025-08-16 20:13:28 +00:00
35a023f449 lpac: 2.2.1 -> 2.3.0 2025-08-16 17:06:56 +00:00
f0aec4416c uassets: 2025-08-15 -> 2025-08-16 2025-08-16 16:53:29 +00:00
e0bb1b7c62 servo: gitea: place only the most expensive repos behind Anubis 2025-08-16 08:15:47 +00:00
9847e0171c flowy: avoid invoking no-op efibootmgr operations 2025-08-16 08:05:55 +00:00
03a1638628 flowy: set nixos as default EFI boot entry, always 2025-08-16 07:55:35 +00:00
f7327bef3e servo: document the anubis openGraph setting 2025-08-16 07:04:56 +00:00
47fb8296db flowy: add bootloader entry to boot into Windows (but i still need to do more config to get Bitlocker to work w/o secure boot) 2025-08-16 07:02:37 +00:00
b409fbb5f7 systemd-boot: enable memtest and edk2 UEFI shell 2025-08-16 07:00:47 +00:00
84092395f4 Merge pull request 'patch-gitea-anubis-opengraph' (#6) from shelvacu/colins-nix-files:patch-gitea-anubis-opengraph into master
Reviewed-on: colin/nix-files#6
2025-08-16 07:00:22 +00:00
115 changed files with 2885 additions and 1481 deletions

View File

@@ -52,13 +52,12 @@
- gnome-calls: retry net connection when DNS is down
- gtk: build schemas even on cross compilation: <https://github.com/NixOS/nixpkgs/pull/247844>
- linux: upstream PinePhonePro device trees
- especially the rt5640 profiles, and in a way which doesn't require alsa-ucm (see my notes in pkgs.pine64-alsa-ucm)
- nwg-panel: configurable media controls
- nwg-panel / playerctl hang fix (i think nwg-panel is what should be patched here)
## IMPROVEMENTS:
- servo: expand /boot to 2 GiB like all other hosts
- moby: port to systemd-boot
- sane-deadlines: show day of the week for upcoming items
- and only show on "first" terminal opened; not on Ctrl+N terminals
- curlftpfs: replace with something better
@@ -74,7 +73,6 @@
- can't do that because lots of applications don't handle URIs
- could workaround using a wrapper that downloads the file and then passes it to the program
- geary: replace with envelope
- likely requires updating envelope to a more recent version (for multi-accounting), and therefore updating libadwaita...
### security/resilience
- /mnt/desko/home, etc, shouldn't include secrets (~/private)
@@ -135,6 +133,7 @@
- Trivia Quiz (https://linuxphoneapps.org/games/io.github.nokse22.trivia-quiz/)
- sane-sync-music: remove empty dirs
- soulseek: install a CLI app usable over ssh
- moby: replace `spot` with its replacement, `riff` (<https://github.com/Diegovsky/riff>)
#### moby
- moby: port battery support to something upstreamable

View File

@@ -1,18 +1,18 @@
## migrating a host to a new drive
1. copy persistent data off of the host:
### 1. copy persistent data off of the host:
```sh
$ mkdir -p mnt old/persist
$ mount /dev/$old mnt
$ rsync -arv mnt/persist/ old/persist/
```
2. flash the new drive
### 2. flash the new drive
```
$ nix-build -A hosts.moby.img
$ dd if=$(readlink ./result) of=/dev/$new bs=4M oflag=direct conv=sync status=progress
```
3. expand the partition and filesystem
### 3.1. expand the partition
```sh
$ cfdisk /dev/$new
# scroll to the last partition
@@ -21,24 +21,29 @@ $ cfdisk /dev/$new
> Write
type "yes"
> Quit
$ btrfs filesystem resize max /dev/$new
```
### 3.2. expand the filesystem
```
$ mkdir -p /mnt/$new
$ mount /dev/$new /mnt/$new
$ btrfs filesystem resize max /mnt/$new
```
4. copy data onto the new host
### 4. copy data onto the new host
```
$ mkdir new
$ mount /dev/$new new
$ mkdir /mnt/$new
$ mount /dev/$new /mnt/$new
# if you want to use btrfs snapshots (e.g. snapper), then create the data directory as a subvolume:
$ sudo btrfs subvolume create new/persist
$ btrfs subvolume create /mnt/$new/persist
# restore the data
$ rsync -arv old/persist/ new/persist/
$ rsync -arv old/persist/ /mnt/$new/persist/
```
5. ensure/fix ownership
### 5. ensure/fix ownership
```
$ chmod -R a+rX new/nix
$ chmod -R a+rX /mnt/$new/nix
# or, let the nix daemon do it:
$ nix copy --no-check-sigs --to new $(nix-build -A hosts.moby)
$ nix copy --no-check-sigs --to /mnt/$new $(nix-build -A hosts.moby)
```
```
6. insert the disk into the system, and boot!
### 6. insert the disk into the system, and boot!

View File

@@ -13,7 +13,4 @@
# TODO: port to `sane.programs` interface
services.xserver.desktopManager.kodi.enable = true;
# /boot space is at a premium, especially with uncompressed kernels. default was 20.
# boot.loader.generic-extlinux-compatible.configurationLimit = 10;
}

View File

@@ -1,4 +1,4 @@
{ ... }:
{ config, lib, ... }:
{
imports = [
./fs.nix
@@ -27,7 +27,7 @@
sane.roles.client = true;
sane.roles.pc = true;
sane.roles.work = true;
sane.services.ollama.enable = true;
sane.services.ollama.enable = lib.mkIf (config.sane.maxBuildCost >= 3) true;
sane.services.wg-home.enable = true;
sane.ovpn.addrV4 = "172.26.55.21";
# sane.ovpn.addrV6 = "fd00:0000:1337:cafe:1111:1111:20c1:a73c";

View File

@@ -1,4 +1,4 @@
{ ... }:
{ lib, pkgs, ... }:
{
imports = [
./fs.nix
@@ -22,4 +22,37 @@
sops.secrets.colin-passwd.neededForUsers = true;
sane.services.rsync-net.enable = true;
# add an entry to boot into Windows, as if it had been launched directly from the BIOS.
boot.loader.systemd-boot.rebootForBitlocker = true;
boot.loader.systemd-boot.windows.primary.efiDeviceHandle = "HD0b";
system.activationScripts.makeDefaultBootEntry = {
text = let
makeDefaultBootEntry = pkgs.writeShellApplication {
name = "makeDefaultBootEntry";
runtimeInputs = with pkgs; [
efibootmgr
gnugrep
];
text = ''
# configure the EFI firmware to boot into NixOS by default.
# do this by querying the active boot entry, and just making that be the default.
# this is needed on flowy because enabling secure boot / booting into Windows
# resets the default boot order; manually reconfiguring that is tiresome.
efi=$(efibootmgr)
bootCurrent=$(echo "$efi" | grep '^BootCurrent: ')
bootCurrent=''${bootCurrent/BootCurrent: /}
bootOrder=$(echo "$efi" | grep '^BootOrder: ')
bootOrder=''${bootOrder/BootOrder: /}
if ! [[ "$bootOrder" =~ ^"$bootCurrent", ]]; then
# booted entry was not the default,
# so prepend it to the boot order:
newBootOrder="$bootCurrent,$bootOrder"
(set -x; efibootmgr -o "$newBootOrder")
fi
'';
};
in lib.getExe makeDefaultBootEntry;
};
}

View File

@@ -58,10 +58,7 @@
sane.programs.mpv.config.defaultProfile = "fast";
# /boot space is at a premium, especially with uncompressed kernels. default was 20.
# boot.loader.generic-extlinux-compatible.configurationLimit = 10;
# TODO: switch to systemd-boot
boot.loader.generic-extlinux-compatible.enable = true;
boot.loader.systemd-boot.enable = false;
# boot.loader.generic-extlinux-compatible.enable = true;
# boot.loader.generic-extlinux-compatible.configurationLimit = 5;
# boot.loader.systemd-boot.enable = false;
}

View File

@@ -16,7 +16,7 @@
fileSystems."/tmp".options = [ "size=32G" ];
fileSystems."/nix" = {
device = "/dev/disk/by-uuid/cc81cca0-3cc7-4d82-a00c-6243af3e7776";
device = "/dev/disk/by-uuid/55555555-eeee-ffff-bbbb-000020250820";
fsType = "btrfs";
options = [
"compress=zstd"
@@ -25,7 +25,7 @@
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/6EE3-4171";
device = "/dev/disk/by-uuid/2025-0820";
fsType = "vfat";
};

View File

@@ -124,7 +124,8 @@
services.anubis.instances."git.uninsane.org" = {
settings.TARGET = "http://127.0.0.1:3000";
botPolicy.openGraph.enabled = true;
# allow IM clients/etc to show embeds/previews, else they just show "please verify you aren't a bot..."
settings.OG_PASSTHROUGH = true;
};
# hosted git (web view and for `git <cmd>` use
@@ -133,8 +134,13 @@
# XXX(2025-07-24): gitea's still being crawled, even with robots.txt.
# the load is less than when Anthropic first started, but it's still pretty high (like 600%).
# place behind anubis to prevent AI crawlers from hogging my CPU (gitea is slow to render pages).
proxyPass = "http://unix:${config.services.anubis.instances."git.uninsane.org".settings.BIND}";
# proxyPass = "http://127.0.0.1:3000";
proxyPassHeavy = "http://unix:${config.services.anubis.instances."git.uninsane.org".settings.BIND}";
# but anubis breaks embeds, so only protect the expensive repos.
proxyPassLight = "http://127.0.0.1:3000";
proxyTo = proxy: root: {
proxyPass = proxy;
recommendedProxySettings = true;
};
in {
forceSSL = true; # gitea complains if served over a different protocol than its config file says
enableACME = true;
@@ -144,9 +150,20 @@
'';
locations."/" = {
inherit proxyPass;
proxyPass = proxyPassLight;
recommendedProxySettings = true;
};
# selectively proxy the heavyweight items through anubis.
# a typical interaction is:
# nginx:/colin/linux -> anubis:/colin/linux -> browser is served a loading page
# -> nginx:.within.website/x/cmd/anubis/api/pass-challenge?response=... -> anubis:.within.website/x/cmd/anubis/api/pass-challenge?response=... -> browser is forwarded to /colin/linux
# -> nginx:/colin/linux -> anubis:/colin/linux -> gitea:/colin/linux -> browser is served the actual content
locations."/.within.website/" = proxyTo proxyPassHeavy;
locations."/colin/linux" = proxyTo proxyPassHeavy;
locations."/colin/nixpkgs" = proxyTo proxyPassHeavy;
locations."/colin/opencellid-mirror" = proxyTo proxyPassHeavy;
locations."/colin/podcastindex-db-mirror" = proxyTo proxyPassHeavy;
# fuck you @anthropic
# locations."= /robots.txt".extraConfig = ''
# return 200 "User-agent: *\nDisallow: /\n";
@@ -154,7 +171,7 @@
# gitea serves all `raw` files as content-type: plain, but i'd like to serve them as their actual content type.
# or at least, enough to make specific pages viewable (serving unoriginal content as arbitrary content type is dangerous).
locations."~ ^/colin/phone-case-cq/raw/.*.html" = {
inherit proxyPass;
proxyPass = proxyPassLight;
recommendedProxySettings = true;
extraConfig = ''
proxy_hide_header Content-Type;
@@ -163,7 +180,7 @@
'';
};
locations."~ ^/colin/phone-case-cq/raw/.*.js" = {
inherit proxyPass;
proxyPass = proxyPassLight;
recommendedProxySettings = true;
extraConfig = ''
proxy_hide_header Content-Type;

View File

@@ -14,158 +14,160 @@
#
# N.B.: default install DOES NOT SUPPORT DLNA out of the box.
# one must install it as a "plugin", which can be done through the UI.
{ ... }:
{ config, lib, ... }:
# lib.mkIf false #< XXX(2024-11-17): disabled because it hasn't been working for months; web UI hangs on load, TVs see no files
{
# https://jellyfin.org/docs/general/networking/index.html
sane.ports.ports."1900" = {
protocol = [ "udp" ];
visibleTo.lan = true;
description = "colin-upnp-for-jellyfin";
};
sane.ports.ports."7359" = {
protocol = [ "udp" ];
visibleTo.lan = true;
description = "colin-jellyfin-specific-client-discovery";
# ^ not sure if this is necessary: copied this port from nixos jellyfin.openFirewall
};
# not sure if 8096/8920 get used either:
sane.ports.ports."8096" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
description = "colin-jellyfin-http-lan";
};
sane.ports.ports."8920" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
description = "colin-jellyfin-https-lan";
};
sane.persist.sys.byStore.plaintext = [
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/data"; method = "bind"; }
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/metadata"; method = "bind"; }
# TODO: ship plugins statically, via nix. that'll be less fragile
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/plugins/DLNA_5.0.0.0"; method = "bind"; }
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/root"; method = "bind"; }
];
sane.persist.sys.byStore.ephemeral = [
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/log"; method = "bind"; }
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/transcodes"; method = "bind"; }
];
services.jellyfin.enable = true;
users.users.jellyfin.extraGroups = [ "media" ];
sane.fs."/var/lib/jellyfin".dir.acl = {
user = "jellyfin";
group = "jellyfin";
mode = "0700";
};
# `"Jellyfin.Plugin.Dlna": "Debug"` logging: <https://jellyfin.org/docs/general/networking/dlna>
# TODO: switch Dlna back to 'Information' once satisfied with stability
sane.fs."/var/lib/jellyfin/config/logging.json".symlink.text = ''
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning",
"Jellyfin.Plugin.Dlna": "Debug"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext", "WithThreadId" ]
}
}
'';
sane.fs."/var/lib/jellyfin/config/network.xml".file.text = ''
<?xml version="1.0" encoding="utf-8"?>
<NetworkConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<BaseUrl />
<EnableHttps>false</EnableHttps>
<RequireHttps>false</RequireHttps>
<InternalHttpPort>8096</InternalHttpPort>
<InternalHttpsPort>8920</InternalHttpsPort>
<PublicHttpPort>8096</PublicHttpPort>
<PublicHttpsPort>8920</PublicHttpsPort>
<AutoDiscovery>true</AutoDiscovery>
<EnableUPnP>false</EnableUPnP>
<EnableIPv4>true</EnableIPv4>
<EnableIPv6>false</EnableIPv6>
<EnableRemoteAccess>true</EnableRemoteAccess>
<LocalNetworkSubnets>
<string>10.78.76.0/22</string>
</LocalNetworkSubnets>
<KnownProxies>
<string>127.0.0.1</string>
<string>localhost</string>
<string>10.78.79.1</string>
</KnownProxies>
<IgnoreVirtualInterfaces>false</IgnoreVirtualInterfaces>
<VirtualInterfaceNames />
<EnablePublishedServerUriByRequest>false</EnablePublishedServerUriByRequest>
<PublishedServerUriBySubnet />
<RemoteIPFilter />
<IsRemoteIPFilterBlacklist>false</IsRemoteIPFilterBlacklist>
</NetworkConfiguration>
'';
# guest user id is `5ad194d60dca41de84b332950ffc4308`
sane.fs."/var/lib/jellyfin/plugins/configurations/Jellyfin.Plugin.Dlna.xml".file.text = ''
<?xml version="1.0" encoding="utf-8"?>
<DlnaPluginConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<EnablePlayTo>true</EnablePlayTo>
<ClientDiscoveryIntervalSeconds>60</ClientDiscoveryIntervalSeconds>
<BlastAliveMessages>true</BlastAliveMessages>
<AliveMessageIntervalSeconds>180</AliveMessageIntervalSeconds>
<SendOnlyMatchedHost>true</SendOnlyMatchedHost>
<DefaultUserId>5ad194d6-0dca-41de-84b3-32950ffc4308</DefaultUserId>
</DlnaPluginConfiguration>
'';
# fix LG TV to play more files.
# there are certain files for which it only supports Direct Play (not even "Direct Stream" -- but "Direct Play").
# this isn't a 100% fix: patching the profile allows e.g. Azumanga Daioh to play,
# but A Place Further Than the Universe still fails as before.
#
# profile is based on upstream: <https://github.com/jellyfin/jellyfin-plugin-dlna>
sane.fs."/var/lib/jellyfin/plugins/DLNA_5.0.0.0/profiles/LG Smart TV.xml".symlink.target = ./dlna/user/LG_Smart_TV.xml;
# XXX(2024-11-17): old method, but the file referenced seems not to be used and setting just it causes failures:
# > [DBG] Jellyfin.Plugin.Dlna.ContentDirectory.ContentDirectoryService: Not eligible for DirectPlay due to unsupported subtitles
# sane.fs."/var/lib/jellyfin/plugins/configurations/dlna/user/LG Smart TV.xml".symlink.target = ./dlna/user/LG_Smart_TV.xml;
systemd.services.jellyfin.unitConfig.RequiresMountsFor = [
"/var/media"
];
# Jellyfin multimedia server
# this is mostly taken from the official jellfin.org docs
services.nginx.virtualHosts."jelly.uninsane.org" = {
forceSSL = true;
enableACME = true;
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:8096";
proxyWebsockets = true;
recommendedProxySettings = true;
# extraConfig = ''
# # Disable buffering when the nginx proxy gets very resource heavy upon streaming
# proxy_buffering off;
# '';
config = lib.mkIf (config.sane.maxBuildCost >= 2) {
# https://jellyfin.org/docs/general/networking/index.html
sane.ports.ports."1900" = {
protocol = [ "udp" ];
visibleTo.lan = true;
description = "colin-upnp-for-jellyfin";
};
sane.ports.ports."7359" = {
protocol = [ "udp" ];
visibleTo.lan = true;
description = "colin-jellyfin-specific-client-discovery";
# ^ not sure if this is necessary: copied this port from nixos jellyfin.openFirewall
};
# not sure if 8096/8920 get used either:
sane.ports.ports."8096" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
description = "colin-jellyfin-http-lan";
};
sane.ports.ports."8920" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
description = "colin-jellyfin-https-lan";
};
};
sane.dns.zones."uninsane.org".inet.CNAME."jelly" = "native";
sane.persist.sys.byStore.plaintext = [
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/data"; method = "bind"; }
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/metadata"; method = "bind"; }
# TODO: ship plugins statically, via nix. that'll be less fragile
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/plugins/DLNA_5.0.0.0"; method = "bind"; }
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/root"; method = "bind"; }
];
sane.persist.sys.byStore.ephemeral = [
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/log"; method = "bind"; }
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/transcodes"; method = "bind"; }
];
services.jellyfin.enable = true;
users.users.jellyfin.extraGroups = [ "media" ];
sane.fs."/var/lib/jellyfin".dir.acl = {
user = "jellyfin";
group = "jellyfin";
mode = "0700";
};
# `"Jellyfin.Plugin.Dlna": "Debug"` logging: <https://jellyfin.org/docs/general/networking/dlna>
# TODO: switch Dlna back to 'Information' once satisfied with stability
sane.fs."/var/lib/jellyfin/config/logging.json".symlink.text = ''
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning",
"Jellyfin.Plugin.Dlna": "Debug"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext", "WithThreadId" ]
}
}
'';
sane.fs."/var/lib/jellyfin/config/network.xml".file.text = ''
<?xml version="1.0" encoding="utf-8"?>
<NetworkConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<BaseUrl />
<EnableHttps>false</EnableHttps>
<RequireHttps>false</RequireHttps>
<InternalHttpPort>8096</InternalHttpPort>
<InternalHttpsPort>8920</InternalHttpsPort>
<PublicHttpPort>8096</PublicHttpPort>
<PublicHttpsPort>8920</PublicHttpsPort>
<AutoDiscovery>true</AutoDiscovery>
<EnableUPnP>false</EnableUPnP>
<EnableIPv4>true</EnableIPv4>
<EnableIPv6>false</EnableIPv6>
<EnableRemoteAccess>true</EnableRemoteAccess>
<LocalNetworkSubnets>
<string>10.78.76.0/22</string>
</LocalNetworkSubnets>
<KnownProxies>
<string>127.0.0.1</string>
<string>localhost</string>
<string>10.78.79.1</string>
</KnownProxies>
<IgnoreVirtualInterfaces>false</IgnoreVirtualInterfaces>
<VirtualInterfaceNames />
<EnablePublishedServerUriByRequest>false</EnablePublishedServerUriByRequest>
<PublishedServerUriBySubnet />
<RemoteIPFilter />
<IsRemoteIPFilterBlacklist>false</IsRemoteIPFilterBlacklist>
</NetworkConfiguration>
'';
# guest user id is `5ad194d60dca41de84b332950ffc4308`
sane.fs."/var/lib/jellyfin/plugins/configurations/Jellyfin.Plugin.Dlna.xml".file.text = ''
<?xml version="1.0" encoding="utf-8"?>
<DlnaPluginConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<EnablePlayTo>true</EnablePlayTo>
<ClientDiscoveryIntervalSeconds>60</ClientDiscoveryIntervalSeconds>
<BlastAliveMessages>true</BlastAliveMessages>
<AliveMessageIntervalSeconds>180</AliveMessageIntervalSeconds>
<SendOnlyMatchedHost>true</SendOnlyMatchedHost>
<DefaultUserId>5ad194d6-0dca-41de-84b3-32950ffc4308</DefaultUserId>
</DlnaPluginConfiguration>
'';
# fix LG TV to play more files.
# there are certain files for which it only supports Direct Play (not even "Direct Stream" -- but "Direct Play").
# this isn't a 100% fix: patching the profile allows e.g. Azumanga Daioh to play,
# but A Place Further Than the Universe still fails as before.
#
# profile is based on upstream: <https://github.com/jellyfin/jellyfin-plugin-dlna>
sane.fs."/var/lib/jellyfin/plugins/DLNA_5.0.0.0/profiles/LG Smart TV.xml".symlink.target = ./dlna/user/LG_Smart_TV.xml;
# XXX(2024-11-17): old method, but the file referenced seems not to be used and setting just it causes failures:
# > [DBG] Jellyfin.Plugin.Dlna.ContentDirectory.ContentDirectoryService: Not eligible for DirectPlay due to unsupported subtitles
# sane.fs."/var/lib/jellyfin/plugins/configurations/dlna/user/LG Smart TV.xml".symlink.target = ./dlna/user/LG_Smart_TV.xml;
systemd.services.jellyfin.unitConfig.RequiresMountsFor = [
"/var/media"
];
# Jellyfin multimedia server
# this is mostly taken from the official jellfin.org docs
services.nginx.virtualHosts."jelly.uninsane.org" = {
forceSSL = true;
enableACME = true;
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:8096";
proxyWebsockets = true;
recommendedProxySettings = true;
# extraConfig = ''
# # Disable buffering when the nginx proxy gets very resource heavy upon streaming
# proxy_buffering off;
# '';
};
};
sane.dns.zones."uninsane.org".inet.CNAME."jelly" = "native";
};
}

View File

@@ -1,40 +1,42 @@
{ pkgs, ... }:
{ config, lib, pkgs, ... }:
{
sane.services.kiwix-serve = {
enable = true;
port = 8013;
zimPaths = with pkgs.zimPackages; [
alpinelinux_en_all_maxi.zimPath
archlinux_en_all_maxi.zimPath
bitcoin_en_all_maxi.zimPath
devdocs_en_nix.zimPath
gentoo_en_all_maxi.zimPath
# khanacademy_en_all.zimPath #< TODO: enable
openstreetmap-wiki_en_all_maxi.zimPath
psychonautwiki_en_all_maxi.zimPath
rationalwiki_en_all_maxi.zimPath
# wikipedia_en_100.zimPath
wikipedia_en_all_maxi.zimPath
# wikipedia_en_all_mini.zimPath
zimgit-food-preparation_en.zimPath
zimgit-medicine_en.zimPath
zimgit-post-disaster_en.zimPath
zimgit-water_en.zimPath
];
};
services.nginx.virtualHosts."w.uninsane.org" = {
forceSSL = true;
enableACME = true;
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:8013";
recommendedProxySettings = true;
config = lib.mkIf (config.sane.maxBuildCost >= 3) {
sane.services.kiwix-serve = {
enable = true;
port = 8013;
zimPaths = with pkgs.zimPackages; [
alpinelinux_en_all_maxi.zimPath
archlinux_en_all_maxi.zimPath
bitcoin_en_all_maxi.zimPath
devdocs_en_nix.zimPath
gentoo_en_all_maxi.zimPath
# khanacademy_en_all.zimPath #< TODO: enable
openstreetmap-wiki_en_all_maxi.zimPath
psychonautwiki_en_all_maxi.zimPath
rationalwiki_en_all_maxi.zimPath
# wikipedia_en_100.zimPath
wikipedia_en_all_maxi.zimPath
# wikipedia_en_all_mini.zimPath
zimgit-food-preparation_en.zimPath
zimgit-medicine_en.zimPath
zimgit-post-disaster_en.zimPath
zimgit-water_en.zimPath
];
};
locations."= /robots.txt".extraConfig = ''
return 200 "User-agent: *\nDisallow: /\n";
'';
};
sane.dns.zones."uninsane.org".inet.CNAME."w" = "native";
services.nginx.virtualHosts."w.uninsane.org" = {
forceSSL = true;
enableACME = true;
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:8013";
recommendedProxySettings = true;
};
locations."= /robots.txt".extraConfig = ''
return 200 "User-agent: *\nDisallow: /\n";
'';
};
sane.dns.zones."uninsane.org".inet.CNAME."w" = "native";
};
}

View File

@@ -3,7 +3,7 @@
# - <repo:LemmyNet/lemmy:docker/nginx.conf>
# - <repo:LemmyNet/lemmy-ansible:templates/nginx.conf>
{ lib, pkgs, ... }:
{ config, lib, pkgs, ... }:
let
uiPort = 1234; # default ui port is 1234
backendPort = 8536; # default backend port is 8536
@@ -24,154 +24,156 @@ let
media.video.max_frame_count = 30 * 60 * 60;
};
in {
services.lemmy = {
enable = true;
settings.hostname = "lemmy.uninsane.org";
# federation.debug forces outbound federation queries to be run synchronously
# N.B.: this option might not be read for 0.17.0+? <https://github.com/LemmyNet/lemmy/blob/c32585b03429f0f76d1e4ff738786321a0a9df98/RELEASES.md#upgrade-instructions>
# settings.federation.debug = true;
settings.port = backendPort;
ui.port = uiPort;
database.createLocally = true;
nginx.enable = true;
};
config = lib.mkIf (config.sane.maxBuildCost >= 2) {
services.lemmy = {
enable = true;
settings.hostname = "lemmy.uninsane.org";
# federation.debug forces outbound federation queries to be run synchronously
# N.B.: this option might not be read for 0.17.0+? <https://github.com/LemmyNet/lemmy/blob/c32585b03429f0f76d1e4ff738786321a0a9df98/RELEASES.md#upgrade-instructions>
# settings.federation.debug = true;
settings.port = backendPort;
ui.port = uiPort;
database.createLocally = true;
nginx.enable = true;
};
systemd.services.lemmy.environment = {
RUST_BACKTRACE = "full";
RUST_LOG = "error";
# RUST_LOG = "warn";
# RUST_LOG = "debug";
# RUST_LOG = "trace";
# upstream defaults LEMMY_DATABASE_URL = "postgres:///lemmy?host=/run/postgresql";
# - Postgres complains that we didn't specify a user
# lemmy formats the url as:
# - postgres://{user}:{password}@{host}:{port}/{database}
# SO suggests (https://stackoverflow.com/questions/3582552/what-is-the-format-for-the-postgresql-connection-string-url):
# - postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]
# LEMMY_DATABASE_URL = "postgres://lemmy@/run/postgresql"; # connection to server on socket "/run/postgresql/.s.PGSQL.5432" failed: FATAL: database "run/postgresql" does not exist
# LEMMY_DATABASE_URL = "postgres://lemmy?host=/run/postgresql"; # no PostgreSQL user name specified in startup packet
# LEMMY_DATABASE_URL = lib.mkForce "postgres://lemmy@?host=/run/postgresql"; # WORKS
LEMMY_DATABASE_URL = lib.mkForce "postgres://lemmy@/lemmy?host=/run/postgresql";
};
users.groups.lemmy = {};
users.users.lemmy = {
group = "lemmy";
isSystemUser = true;
};
systemd.services.lemmy.environment = {
RUST_BACKTRACE = "full";
RUST_LOG = "error";
# RUST_LOG = "warn";
# RUST_LOG = "debug";
# RUST_LOG = "trace";
# upstream defaults LEMMY_DATABASE_URL = "postgres:///lemmy?host=/run/postgresql";
# - Postgres complains that we didn't specify a user
# lemmy formats the url as:
# - postgres://{user}:{password}@{host}:{port}/{database}
# SO suggests (https://stackoverflow.com/questions/3582552/what-is-the-format-for-the-postgresql-connection-string-url):
# - postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]
# LEMMY_DATABASE_URL = "postgres://lemmy@/run/postgresql"; # connection to server on socket "/run/postgresql/.s.PGSQL.5432" failed: FATAL: database "run/postgresql" does not exist
# LEMMY_DATABASE_URL = "postgres://lemmy?host=/run/postgresql"; # no PostgreSQL user name specified in startup packet
# LEMMY_DATABASE_URL = lib.mkForce "postgres://lemmy@?host=/run/postgresql"; # WORKS
LEMMY_DATABASE_URL = lib.mkForce "postgres://lemmy@/lemmy?host=/run/postgresql";
};
users.groups.lemmy = {};
users.users.lemmy = {
group = "lemmy";
isSystemUser = true;
};
services.nginx.virtualHosts."lemmy.uninsane.org" = {
forceSSL = true;
enableACME = true;
};
services.nginx.virtualHosts."lemmy.uninsane.org" = {
forceSSL = true;
enableACME = true;
};
sane.dns.zones."uninsane.org".inet.CNAME."lemmy" = "native";
sane.dns.zones."uninsane.org".inet.CNAME."lemmy" = "native";
systemd.services.lemmy = {
# fix to use a normal user so we can configure perms correctly
# XXX(2024-07-28): this hasn't been rigorously tested:
# possible that i've set something too strict and won't notice right away
serviceConfig.DynamicUser = lib.mkForce false;
serviceConfig.User = "lemmy";
serviceConfig.Group = "lemmy";
systemd.services.lemmy = {
# fix to use a normal user so we can configure perms correctly
# XXX(2024-07-28): this hasn't been rigorously tested:
# possible that i've set something too strict and won't notice right away
serviceConfig.DynamicUser = lib.mkForce false;
serviceConfig.User = "lemmy";
serviceConfig.Group = "lemmy";
# switch postgres from Requires -> Wants, so that postgres may restart without taking lemmy down with it.
requires = lib.mkForce [];
wants = [ "postgresql.service" ];
# switch postgres from Requires -> Wants, so that postgres may restart without taking lemmy down with it.
requires = lib.mkForce [];
wants = [ "postgresql.service" ];
# hardening (systemd-analyze security lemmy)
# a handful of these are specified in upstream nixpkgs, but mostly not
serviceConfig.LockPersonality = true;
serviceConfig.NoNewPrivileges = true;
serviceConfig.MemoryDenyWriteExecute = true;
serviceConfig.PrivateDevices = true;
serviceConfig.PrivateMounts = true;
serviceConfig.PrivateTmp = true;
serviceConfig.PrivateUsers = true;
serviceConfig.ProcSubset = "pid";
# hardening (systemd-analyze security lemmy)
# a handful of these are specified in upstream nixpkgs, but mostly not
serviceConfig.LockPersonality = true;
serviceConfig.NoNewPrivileges = true;
serviceConfig.MemoryDenyWriteExecute = true;
serviceConfig.PrivateDevices = true;
serviceConfig.PrivateMounts = true;
serviceConfig.PrivateTmp = true;
serviceConfig.PrivateUsers = true;
serviceConfig.ProcSubset = "pid";
serviceConfig.ProtectClock = true;
serviceConfig.ProtectControlGroups = true;
serviceConfig.ProtectHome = true;
serviceConfig.ProtectHostname = true;
serviceConfig.ProtectKernelLogs = true;
serviceConfig.ProtectKernelModules = true;
serviceConfig.ProtectKernelTunables = true;
serviceConfig.ProtectProc = "invisible";
serviceConfig.ProtectSystem = "strict";
serviceConfig.RemoveIPC = true;
serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
serviceConfig.ProtectClock = true;
serviceConfig.ProtectControlGroups = true;
serviceConfig.ProtectHome = true;
serviceConfig.ProtectHostname = true;
serviceConfig.ProtectKernelLogs = true;
serviceConfig.ProtectKernelModules = true;
serviceConfig.ProtectKernelTunables = true;
serviceConfig.ProtectProc = "invisible";
serviceConfig.ProtectSystem = "strict";
serviceConfig.RemoveIPC = true;
serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
serviceConfig.RestrictNamespaces = true;
serviceConfig.RestrictSUIDSGID = true;
serviceConfig.SystemCallArchitectures = "native";
serviceConfig.SystemCallFilter = [ "@system-service" ];
};
serviceConfig.RestrictNamespaces = true;
serviceConfig.RestrictSUIDSGID = true;
serviceConfig.SystemCallArchitectures = "native";
serviceConfig.SystemCallFilter = [ "@system-service" ];
};
systemd.services.lemmy-ui = {
# hardening (systemd-analyze security lemmy-ui)
# TODO: upstream into nixpkgs
serviceConfig.LockPersonality = true;
serviceConfig.NoNewPrivileges = true;
# serviceConfig.MemoryDenyWriteExecute = true; #< it uses v8, JIT
serviceConfig.PrivateDevices = true;
serviceConfig.PrivateMounts = true;
serviceConfig.PrivateTmp = true;
serviceConfig.PrivateUsers = true;
serviceConfig.ProcSubset = "pid";
systemd.services.lemmy-ui = {
# hardening (systemd-analyze security lemmy-ui)
# TODO: upstream into nixpkgs
serviceConfig.LockPersonality = true;
serviceConfig.NoNewPrivileges = true;
# serviceConfig.MemoryDenyWriteExecute = true; #< it uses v8, JIT
serviceConfig.PrivateDevices = true;
serviceConfig.PrivateMounts = true;
serviceConfig.PrivateTmp = true;
serviceConfig.PrivateUsers = true;
serviceConfig.ProcSubset = "pid";
serviceConfig.ProtectClock = true;
serviceConfig.ProtectControlGroups = true;
serviceConfig.ProtectHome = true;
serviceConfig.ProtectHostname = true;
serviceConfig.ProtectKernelLogs = true;
serviceConfig.ProtectKernelModules = true;
serviceConfig.ProtectKernelTunables = true;
serviceConfig.ProtectProc = "invisible";
serviceConfig.ProtectSystem = "strict";
serviceConfig.RemoveIPC = true;
serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
serviceConfig.ProtectClock = true;
serviceConfig.ProtectControlGroups = true;
serviceConfig.ProtectHome = true;
serviceConfig.ProtectHostname = true;
serviceConfig.ProtectKernelLogs = true;
serviceConfig.ProtectKernelModules = true;
serviceConfig.ProtectKernelTunables = true;
serviceConfig.ProtectProc = "invisible";
serviceConfig.ProtectSystem = "strict";
serviceConfig.RemoveIPC = true;
serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
serviceConfig.RestrictNamespaces = true;
serviceConfig.RestrictSUIDSGID = true;
serviceConfig.SystemCallArchitectures = "native";
serviceConfig.SystemCallFilter = [ "@system-service" "@pkey" "@sandbox" ];
};
serviceConfig.RestrictNamespaces = true;
serviceConfig.RestrictSUIDSGID = true;
serviceConfig.SystemCallArchitectures = "native";
serviceConfig.SystemCallFilter = [ "@system-service" "@pkey" "@sandbox" ];
};
#v DO NOT REMOVE: defaults to 0.3, instead of latest, so always need to explicitly set this.
services.pict-rs.package = pict-rs;
#v DO NOT REMOVE: defaults to 0.3, instead of latest, so always need to explicitly set this.
services.pict-rs.package = pict-rs;
systemd.services.pict-rs = {
serviceConfig.ExecStart = lib.mkForce (lib.concatStringsSep " " [
(lib.getExe pict-rs)
"--config-file"
tomlConfig
"run"
]);
systemd.services.pict-rs = {
serviceConfig.ExecStart = lib.mkForce (lib.concatStringsSep " " [
(lib.getExe pict-rs)
"--config-file"
tomlConfig
"run"
]);
# hardening (systemd-analyze security pict-rs)
# TODO: upstream into nixpkgs
serviceConfig.LockPersonality = true;
serviceConfig.NoNewPrivileges = true;
serviceConfig.MemoryDenyWriteExecute = true;
serviceConfig.PrivateDevices = true;
serviceConfig.PrivateMounts = true;
serviceConfig.PrivateTmp = true;
serviceConfig.PrivateUsers = true;
serviceConfig.ProcSubset = "pid";
serviceConfig.ProtectClock = true;
serviceConfig.ProtectControlGroups = true;
serviceConfig.ProtectHome = true;
serviceConfig.ProtectHostname = true;
serviceConfig.ProtectKernelLogs = true;
serviceConfig.ProtectKernelModules = true;
serviceConfig.ProtectKernelTunables = true;
serviceConfig.ProtectProc = "invisible";
serviceConfig.ProtectSystem = "strict";
serviceConfig.RemoveIPC = true;
serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
serviceConfig.RestrictNamespaces = true;
serviceConfig.RestrictSUIDSGID = true;
serviceConfig.SystemCallArchitectures = "native";
serviceConfig.SystemCallFilter = [ "@system-service" ];
# hardening (systemd-analyze security pict-rs)
# TODO: upstream into nixpkgs
serviceConfig.LockPersonality = true;
serviceConfig.NoNewPrivileges = true;
serviceConfig.MemoryDenyWriteExecute = true;
serviceConfig.PrivateDevices = true;
serviceConfig.PrivateMounts = true;
serviceConfig.PrivateTmp = true;
serviceConfig.PrivateUsers = true;
serviceConfig.ProcSubset = "pid";
serviceConfig.ProtectClock = true;
serviceConfig.ProtectControlGroups = true;
serviceConfig.ProtectHome = true;
serviceConfig.ProtectHostname = true;
serviceConfig.ProtectKernelLogs = true;
serviceConfig.ProtectKernelModules = true;
serviceConfig.ProtectKernelTunables = true;
serviceConfig.ProtectProc = "invisible";
serviceConfig.ProtectSystem = "strict";
serviceConfig.RemoveIPC = true;
serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
serviceConfig.RestrictNamespaces = true;
serviceConfig.RestrictSUIDSGID = true;
serviceConfig.SystemCallArchitectures = "native";
serviceConfig.SystemCallFilter = [ "@system-service" ];
};
};
}

View File

@@ -14,207 +14,209 @@ let
# logLevel = "debug";
in
{
sane.persist.sys.byStore.private = [
# contains media i've uploaded to the server
{ user = "pleroma"; group = "pleroma"; path = "/var/lib/pleroma"; method = "bind"; }
];
services.pleroma.enable = true;
services.pleroma.secretConfigFile = config.sops.secrets.pleroma_secrets.path;
services.pleroma.configs = [
''
import Config
config = lib.mkIf (config.sane.maxBuildCost >= 2) {
sane.persist.sys.byStore.private = [
# contains media i've uploaded to the server
{ user = "pleroma"; group = "pleroma"; path = "/var/lib/pleroma"; method = "bind"; }
];
services.pleroma.enable = true;
services.pleroma.secretConfigFile = config.sops.secrets.pleroma_secrets.path;
services.pleroma.configs = [
''
import Config
config :pleroma, Pleroma.Web.Endpoint,
url: [host: "fed.uninsane.org", scheme: "https", port: 443],
http: [ip: {127, 0, 0, 1}, port: 4040]
# secret_key_base: "{secrets.pleroma.secret_key_base}",
# signing_salt: "{secrets.pleroma.signing_salt}"
config :pleroma, Pleroma.Web.Endpoint,
url: [host: "fed.uninsane.org", scheme: "https", port: 443],
http: [ip: {127, 0, 0, 1}, port: 4040]
# secret_key_base: "{secrets.pleroma.secret_key_base}",
# signing_salt: "{secrets.pleroma.signing_salt}"
config :pleroma, :instance,
name: "Perfectly Sane",
description: "Single-user Pleroma instance",
email: "admin.pleroma@uninsane.org",
notify_email: "notify.pleroma@uninsane.org",
limit: 5000,
registrations_open: true,
account_approval_required: true,
max_pinned_statuses: 5,
external_user_synchronization: true
config :pleroma, :instance,
name: "Perfectly Sane",
description: "Single-user Pleroma instance",
email: "admin.pleroma@uninsane.org",
notify_email: "notify.pleroma@uninsane.org",
limit: 5000,
registrations_open: true,
account_approval_required: true,
max_pinned_statuses: 5,
external_user_synchronization: true
# docs: https://hexdocs.pm/swoosh/Swoosh.Adapters.Sendmail.html
# test mail config with sudo -u pleroma ./bin/pleroma_ctl email test --to someone@somewhere.net
config :pleroma, Pleroma.Emails.Mailer,
enabled: true,
adapter: Swoosh.Adapters.Sendmail,
cmd_path: "${lib.getExe' pkgs.postfix "sendmail"}"
# docs: https://hexdocs.pm/swoosh/Swoosh.Adapters.Sendmail.html
# test mail config with sudo -u pleroma ./bin/pleroma_ctl email test --to someone@somewhere.net
config :pleroma, Pleroma.Emails.Mailer,
enabled: true,
adapter: Swoosh.Adapters.Sendmail,
cmd_path: "${lib.getExe' pkgs.postfix "sendmail"}"
config :pleroma, Pleroma.User,
restricted_nicknames: [ "admin", "uninsane", "root" ]
config :pleroma, Pleroma.User,
restricted_nicknames: [ "admin", "uninsane", "root" ]
config :pleroma, :media_proxy,
enabled: false,
redirect_on_failure: true
#base_url: "https://cache.pleroma.social"
config :pleroma, :media_proxy,
enabled: false,
redirect_on_failure: true
#base_url: "https://cache.pleroma.social"
# see for reference:
# - `force_custom_plan`: <https://docs.pleroma.social/backend/configuration/postgresql/#disable-generic-query-plans>
config :pleroma, Pleroma.Repo,
adapter: Ecto.Adapters.Postgres,
username: "pleroma",
database: "pleroma",
hostname: "localhost",
pool_size: 10,
prepare: :named,
parameters: [
plan_cache_mode: "force_custom_plan"
]
# XXX: prepare: :named is needed only for PG <= 12
# prepare: :named,
# password: "{secrets.pleroma.db_password}",
# see for reference:
# - `force_custom_plan`: <https://docs.pleroma.social/backend/configuration/postgresql/#disable-generic-query-plans>
config :pleroma, Pleroma.Repo,
adapter: Ecto.Adapters.Postgres,
username: "pleroma",
database: "pleroma",
hostname: "localhost",
pool_size: 10,
prepare: :named,
parameters: [
plan_cache_mode: "force_custom_plan"
]
# XXX: prepare: :named is needed only for PG <= 12
# prepare: :named,
# password: "{secrets.pleroma.db_password}",
# Configure web push notifications
config :web_push_encryption, :vapid_details,
subject: "mailto:notify.pleroma@uninsane.org"
# public_key: "{secrets.pleroma.vapid_public_key}",
# private_key: "{secrets.pleroma.vapid_private_key}"
# Configure web push notifications
config :web_push_encryption, :vapid_details,
subject: "mailto:notify.pleroma@uninsane.org"
# public_key: "{secrets.pleroma.vapid_public_key}",
# private_key: "{secrets.pleroma.vapid_private_key}"
# config :joken, default_signer: "{secrets.pleroma.joken_default_signer}"
# config :joken, default_signer: "{secrets.pleroma.joken_default_signer}"
config :pleroma, :database, rum_enabled: false
config :pleroma, :instance, static_dir: "/var/lib/pleroma/instance/static"
config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
config :pleroma, configurable_from_database: false
config :pleroma, :database, rum_enabled: false
config :pleroma, :instance, static_dir: "/var/lib/pleroma/instance/static"
config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
config :pleroma, configurable_from_database: false
# strip metadata from uploaded images
config :pleroma, Pleroma.Upload, filters: [Pleroma.Upload.Filter.Exiftool.StripLocation]
# strip metadata from uploaded images
config :pleroma, Pleroma.Upload, filters: [Pleroma.Upload.Filter.Exiftool.StripLocation]
# fix log spam: <https://git.pleroma.social/pleroma/pleroma/-/issues/1659>
# specifically, remove LAN addresses from `reserved`
config :pleroma, Pleroma.Web.Plugs.RemoteIp,
enabled: true,
reserved: ["127.0.0.0/8", "::1/128", "fc00::/7", "172.16.0.0/12"]
# fix log spam: <https://git.pleroma.social/pleroma/pleroma/-/issues/1659>
# specifically, remove LAN addresses from `reserved`
config :pleroma, Pleroma.Web.Plugs.RemoteIp,
enabled: true,
reserved: ["127.0.0.0/8", "::1/128", "fc00::/7", "172.16.0.0/12"]
# TODO: GET /api/pleroma/captcha is broken
# there was a nixpkgs PR to fix this around 2022/10 though.
config :pleroma, Pleroma.Captcha,
enabled: false,
method: Pleroma.Captcha.Native
# TODO: GET /api/pleroma/captcha is broken
# there was a nixpkgs PR to fix this around 2022/10 though.
config :pleroma, Pleroma.Captcha,
enabled: false,
method: Pleroma.Captcha.Native
# (enabled by colin)
# Enable Strict-Transport-Security once SSL is working:
config :pleroma, :http_security,
sts: true
# (enabled by colin)
# Enable Strict-Transport-Security once SSL is working:
config :pleroma, :http_security,
sts: true
# docs: https://docs.pleroma.social/backend/configuration/cheatsheet/#logger
config :logger,
backends: [{ExSyslogger, :ex_syslogger}]
# docs: https://docs.pleroma.social/backend/configuration/cheatsheet/#logger
config :logger,
backends: [{ExSyslogger, :ex_syslogger}]
config :logger, :ex_syslogger,
level: :${logLevel}
config :logger, :ex_syslogger,
level: :${logLevel}
# policies => list of message rewriting facilities to be enabled
# transparence => whether to publish these rules in node_info (and /about)
config :pleroma, :mrf,
policies: [Pleroma.Web.ActivityPub.MRF.SimplePolicy],
transparency: true
# policies => list of message rewriting facilities to be enabled
# transparence => whether to publish these rules in node_info (and /about)
config :pleroma, :mrf,
policies: [Pleroma.Web.ActivityPub.MRF.SimplePolicy],
transparency: true
# reject => { host, reason }
config :pleroma, :mrf_simple,
reject: [ {"threads.net", "megacorp"}, {"*.threads.net", "megacorp"} ]
# reject: [ [host: "threads.net", reason: "megacorp"], [host: "*.threads.net", reason: "megacorp"] ]
# reject => { host, reason }
config :pleroma, :mrf_simple,
reject: [ {"threads.net", "megacorp"}, {"*.threads.net", "megacorp"} ]
# reject: [ [host: "threads.net", reason: "megacorp"], [host: "*.threads.net", reason: "megacorp"] ]
# XXX colin: not sure if this actually _does_ anything
# better to steal emoji from other instances?
# - <https://docs.pleroma.social/backend/configuration/cheatsheet/#mrf_steal_emoji>
config :pleroma, :emoji,
shortcode_globs: ["/emoji/**/*.png"],
groups: [
"Cirno": "/emoji/cirno/*.png",
"Kirby": "/emoji/kirby/*.png",
"Bun": "/emoji/bun/*.png",
"Yuru Camp": "/emoji/yuru_camp/*.png",
]
''
];
# XXX colin: not sure if this actually _does_ anything
# better to steal emoji from other instances?
# - <https://docs.pleroma.social/backend/configuration/cheatsheet/#mrf_steal_emoji>
config :pleroma, :emoji,
shortcode_globs: ["/emoji/**/*.png"],
groups: [
"Cirno": "/emoji/cirno/*.png",
"Kirby": "/emoji/kirby/*.png",
"Bun": "/emoji/bun/*.png",
"Yuru Camp": "/emoji/yuru_camp/*.png",
]
''
];
systemd.services.pleroma.path = [
# something inside pleroma invokes `sh` w/o specifying it by path, so this is needed to allow pleroma to start
pkgs.bash
# used by Pleroma to strip geo tags from uploads
pkgs.exiftool
# config.sane.programs.exiftool.package #< XXX(2024-10-20): breaks image uploading
# i saw some errors when pleroma was shutting down about it not being able to find `awk`. probably not critical
# config.sane.programs.gawk.package
# needed for email operations like password reset
pkgs.postfix
];
systemd.services.pleroma.path = [
# something inside pleroma invokes `sh` w/o specifying it by path, so this is needed to allow pleroma to start
pkgs.bash
# used by Pleroma to strip geo tags from uploads
pkgs.exiftool
# config.sane.programs.exiftool.package #< XXX(2024-10-20): breaks image uploading
# i saw some errors when pleroma was shutting down about it not being able to find `awk`. probably not critical
# config.sane.programs.gawk.package
# needed for email operations like password reset
pkgs.postfix
];
systemd.services.pleroma = {
# postgres can be slow to service early requests, preventing pleroma from starting on the first try
serviceConfig.Restart = "on-failure";
serviceConfig.RestartSec = "10s";
systemd.services.pleroma = {
# postgres can be slow to service early requests, preventing pleroma from starting on the first try
serviceConfig.Restart = "on-failure";
serviceConfig.RestartSec = "10s";
# hardening (systemd-analyze security pleroma)
# XXX(2024-07-28): this hasn't been rigorously tested:
# possible that i've set something too strict and won't notice right away
# make sure to test:
# - image/media uploading
serviceConfig.CapabilityBoundingSet = lib.mkForce [ "" "" ]; # nixos default is `~CAP_SYS_ADMIN`
serviceConfig.LockPersonality = true;
serviceConfig.NoNewPrivileges = true;
serviceConfig.MemoryDenyWriteExecute = true;
serviceConfig.PrivateDevices = lib.mkForce true; #< dunno why nixpkgs has this set false; it seems to work as true
serviceConfig.PrivateMounts = true;
serviceConfig.PrivateTmp = true;
serviceConfig.PrivateUsers = true;
# hardening (systemd-analyze security pleroma)
# XXX(2024-07-28): this hasn't been rigorously tested:
# possible that i've set something too strict and won't notice right away
# make sure to test:
# - image/media uploading
serviceConfig.CapabilityBoundingSet = lib.mkForce [ "" "" ]; # nixos default is `~CAP_SYS_ADMIN`
serviceConfig.LockPersonality = true;
serviceConfig.NoNewPrivileges = true;
serviceConfig.MemoryDenyWriteExecute = true;
serviceConfig.PrivateDevices = lib.mkForce true; #< dunno why nixpkgs has this set false; it seems to work as true
serviceConfig.PrivateMounts = true;
serviceConfig.PrivateTmp = true;
serviceConfig.PrivateUsers = true;
serviceConfig.ProtectProc = "invisible";
serviceConfig.ProcSubset = "all"; #< needs /proc/sys/kernel/overflowuid for bwrap
serviceConfig.ProtectProc = "invisible";
serviceConfig.ProcSubset = "all"; #< needs /proc/sys/kernel/overflowuid for bwrap
serviceConfig.ProtectClock = true;
serviceConfig.ProtectControlGroups = true;
serviceConfig.ProtectHome = true;
serviceConfig.ProtectKernelModules = true;
serviceConfig.ProtectSystem = lib.mkForce "strict";
serviceConfig.RemoveIPC = true;
serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
serviceConfig.ProtectClock = true;
serviceConfig.ProtectControlGroups = true;
serviceConfig.ProtectHome = true;
serviceConfig.ProtectKernelModules = true;
serviceConfig.ProtectSystem = lib.mkForce "strict";
serviceConfig.RemoveIPC = true;
serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
serviceConfig.RestrictSUIDSGID = true;
serviceConfig.SystemCallArchitectures = "native";
serviceConfig.SystemCallFilter = [ "@system-service" "@mount" "@sandbox" ]; #< "sandbox" might not actually be necessary
serviceConfig.RestrictSUIDSGID = true;
serviceConfig.SystemCallArchitectures = "native";
serviceConfig.SystemCallFilter = [ "@system-service" "@mount" "@sandbox" ]; #< "sandbox" might not actually be necessary
serviceConfig.ProtectHostname = false; #< else brap can't mount /proc
serviceConfig.ProtectKernelLogs = false; #< else breaks exiftool ("bwrap: Can't mount proc on /newroot/proc: Operation not permitted")
serviceConfig.ProtectKernelTunables = false; #< else breaks exiftool
serviceConfig.RestrictNamespaces = false; # media uploads require bwrap
};
serviceConfig.ProtectHostname = false; #< else brap can't mount /proc
serviceConfig.ProtectKernelLogs = false; #< else breaks exiftool ("bwrap: Can't mount proc on /newroot/proc: Operation not permitted")
serviceConfig.ProtectKernelTunables = false; #< else breaks exiftool
serviceConfig.RestrictNamespaces = false; # media uploads require bwrap
};
# this is required to allow pleroma to send email.
# raw `sendmail` works, but i think pleroma's passing it some funny flags or something, idk.
# hack to fix that.
users.users.pleroma.extraGroups = [ "postdrop" ];
# this is required to allow pleroma to send email.
# raw `sendmail` works, but i think pleroma's passing it some funny flags or something, idk.
# hack to fix that.
users.users.pleroma.extraGroups = [ "postdrop" ];
# Pleroma server and web interface
# TODO: enable publog?
services.nginx.virtualHosts."fed.uninsane.org" = {
forceSSL = true; # pleroma redirects to https anyway
enableACME = true;
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:4040";
recommendedProxySettings = true;
# documented: https://git.pleroma.social/pleroma/pleroma/-/blob/develop/installation/pleroma.nginx
extraConfig = ''
# client_max_body_size defines the maximum upload size
client_max_body_size 16m;
'';
# Pleroma server and web interface
# TODO: enable publog?
services.nginx.virtualHosts."fed.uninsane.org" = {
forceSSL = true; # pleroma redirects to https anyway
enableACME = true;
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:4040";
recommendedProxySettings = true;
# documented: https://git.pleroma.social/pleroma/pleroma/-/blob/develop/installation/pleroma.nginx
extraConfig = ''
# client_max_body_size defines the maximum upload size
client_max_body_size 16m;
'';
};
};
sane.dns.zones."uninsane.org".inet.CNAME."fed" = "native";
sops.secrets."pleroma_secrets" = {
owner = config.users.users.pleroma.name;
};
};
sane.dns.zones."uninsane.org".inet.CNAME."fed" = "native";
sops.secrets."pleroma_secrets" = {
owner = config.users.users.pleroma.name;
};
}

View File

@@ -41,6 +41,10 @@
# - maybe i need to setup stun/turn
#
# TODO:
# - MIGRATE TO NIXOS MODULE OPTIONS:
# - `services.prosody.ssl.`...
# - `services.prosody.log`
# - this decreases likelihood of breakage during future upgrades
# - enable push notifications (mod_cloud_notify)
# - optimize coturn (e.g. move off of the VPN!)
# - ensure muc is working
@@ -245,11 +249,11 @@ in
extraConfig = ''
local function readAll(file)
local f = assert(io.open(file, "rb"))
local f = Lua.assert(Lua.io.open(file, "rb"))
local content = f:read("*all")
f:close()
-- remove trailing newline
return string.gsub(content, "%s+", "")
return Lua.string.gsub(content, "%s+", "")
end
-- logging docs:
@@ -261,9 +265,11 @@ in
}
-- see: <https://prosody.im/doc/certificates#automatic_location>
-- try to solve: "certmanager: Error indexing certificate directory /etc/prosody/certs: cannot open /etc/prosody/certs: No such file or directory"
-- try to solve: "certmanager: Error indexing certificate directory /run/prosody/certs: cannot open /run/prosody/certs: No such file or directory"
-- only, this doesn't work because prosody doesn't like acme's naming scheme
-- certificates = "/var/lib/acme"
-- certificates = "/var/lib/acme/uninsane.org"
-- instead, point to /etc/prosody/certs and configure symlinks into this dir (see nix config)
certificates = "/etc/prosody/certs"
c2s_direct_tls_ports = { 5223 }
s2s_direct_tls_ports = { 5270 }

View File

@@ -6,6 +6,7 @@ let
# some do this via peer-id (e.g. baka); others via user-agent (e.g. MAM).
# peer-id format is essentially the same between 3.00 and 4.x (just swap the MAJOR/MINOR/PATCH numbers).
# user-agent format has changed. `Transmission/3.00` (old) v.s. `TRANSMISSION/MAJ.MIN.PATCH` (new).
# package = pkgs.transmission_3;
realTransmission = pkgs.transmission_4;
realVersion = {
major = lib.versions.major realTransmission.version;

View File

@@ -20,8 +20,33 @@
# - TR_DRY_RUN=1
# - TR_DEBUG=1
set -eu
DOWNLOAD_DIR=/var/media/torrents
usage() {
echo "torrent-done"
echo " transmission torrent-done hook which moves torrents into expected location"
echo " and de-dupes media"
}
parseArgs() {
while [ "$#" -ne 0 ]; do
local arg=$1
shift
case $arg in
(--help)
usage
exit 0
;;
(*)
usage
exit 1
;;
esac
done
}
destructive() {
if [ -n "${TR_DRY_RUN-}" ]; then
echo "[dry-run] $*"
@@ -36,6 +61,8 @@ debug() {
fi
}
parseArgs "$@"
echo "TR_TORRENT_DIR=$TR_TORRENT_DIR TR_TORRENT_NAME=$TR_TORRENT_NAME torrent-done $*"
if [[ "$TR_TORRENT_DIR" =~ ^.*freeleech.*$ ]]; then

View File

@@ -1,14 +1,15 @@
{ lib, pkgs, ... }:
{ config, lib, pkgs, ... }:
{
boot.initrd.supportedFilesystems = [ "ext4" "btrfs" "ext2" "ext3" "vfat" ];
# useful emergency utils
boot.initrd.extraUtilsCommands = ''
copy_bin_and_libs ${lib.getExe' pkgs.btrfs-progs "btrfstune"}
copy_bin_and_libs ${lib.getExe' pkgs.util-linux "{cfdisk,lsblk,lscpu}"}
copy_bin_and_libs ${lib.getExe' pkgs.gptfdisk "{cgdisk,gdisk}"}
copy_bin_and_libs ${lib.getExe' pkgs.smartmontools "smartctl"}
copy_bin_and_libs ${lib.getExe' pkgs.e2fsprogs "resize2fs"}
copy_bin_and_libs ${lib.getExe' pkgs.gptfdisk "{cgdisk,gdisk}"}
copy_bin_and_libs ${lib.getExe' pkgs.mtools "mlabel"}
copy_bin_and_libs ${lib.getExe pkgs.nvme-cli}
copy_bin_and_libs ${lib.getExe' pkgs.smartmontools "smartctl"}
copy_bin_and_libs ${lib.getExe' pkgs.util-linux "{cfdisk,lsblk,lscpu}"}
'';
boot.kernelParams = [
"boot.shell_on_fail"
@@ -37,8 +38,16 @@
boot.consoleLogLevel = 7;
boot.loader.grub.enable = lib.mkDefault false;
# boot.loader.generic-extlinux-compatible.enable = lib.mkDefault true;
boot.loader.systemd-boot.enable = lib.mkDefault true;
boot.loader.systemd-boot.configurationLimit = lib.mkDefault 20;
boot.loader.systemd-boot.edk2-uefi-shell.enable = lib.mkDefault true;
boot.loader.systemd-boot.memtest86.enable = lib.mkDefault
(lib.meta.availableOn pkgs.stdenv.hostPlatform pkgs.memtest86plus);
warnings = lib.optionals (config.boot.loader.systemd-boot.enable && config.hardware.deviceTree.package != null && config.hardware.deviceTree.name == null) [
("systemd-boot enabled on a device-tree-enabled system but without configuring hardware.deviceTree.name: " +
"system will boot against the platform firmware's .dtb instead of the kernel's more up-to-date dtb")
];
hardware.enableAllFirmware = true; # firmware with licenses that don't allow for redistribution. fuck lawyers, fuck IP, give me the goddamn firmware.
# hardware.enableRedistributableFirmware = true; # proprietary but free-to-distribute firmware (extraneous to `enableAllFirmware` option)

View File

@@ -127,6 +127,7 @@ let
(fromDb "talesfromthebridge.buzzsprout.com" // tech) # Sci-Fi? has Peter Watts; author of No Moods, Ads or Cutesy Fucking Icons (rifters.com)
(fromDb "techtalesshow.com" // tech) # Corbin Davenport
(fromDb "theamphour.com" // tech) # The Amp Hour
(fromDb "thefinalstrawradio.noblogs.org/podcasting" // pol)
(fromDb "the-ben-marc-show.simplecast.com" // tech // pol) # Ben Horowitz + Marc Andreessen; love to hate em
(fromDb "timclicks.dev/compose-podcast" // tech) # Rust-heavy dev interviews
(fromDb "werenotwrong.fireside.fm" // pol) # We're Not Wrong

View File

@@ -77,9 +77,9 @@ let
in
lib.mkMerge [
(ifSshAuthorized (remoteHome "crappy" {}))
(ifSshAuthorized (remoteHome "desko" {}))
(ifSshAuthorized (remoteHome "desko" { host = "desko-hn"; }))
(ifSshAuthorized (remoteHome "flowy" {}))
(ifSshAuthorized (remoteHome "lappy" {}))
# (ifSshAuthorized (remoteHome "lappy" {}))
(ifSshAuthorized (remoteHome "moby" { host = "moby-hn"; }))
(ifSshAuthorized (remoteHome "servo" {}))
]

View File

@@ -32,13 +32,13 @@
lan-ip = "10.78.79.56";
};
sane.hosts.by-name."lappy" = {
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDpmFdNSVPRol5hkbbCivRhyeENzb9HVyf9KutGLP2Zu";
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSJnqmVl9/SYQ0btvGb0REwwWY8wkdkGXQZfn/1geEc";
wg-home.pubkey = "FTUWGw2p4/cEcrrIE86PWVnqctbv8OYpw8Gt3+dC/lk=";
wg-home.ip = "10.0.10.20";
lan-ip = "10.78.79.53";
};
# sane.hosts.by-name."lappy" = {
# ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDpmFdNSVPRol5hkbbCivRhyeENzb9HVyf9KutGLP2Zu";
# ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSJnqmVl9/SYQ0btvGb0REwwWY8wkdkGXQZfn/1geEc";
# wg-home.pubkey = "FTUWGw2p4/cEcrrIE86PWVnqctbv8OYpw8Gt3+dC/lk=";
# wg-home.ip = "10.0.10.20";
# lan-ip = "10.78.79.53";
# };
sane.hosts.by-name."moby" = {
# ssh.authorized = lib.mkDefault false; # moby's too easy to hijack: don't let it ssh places

View File

@@ -97,6 +97,8 @@
sane.ids.named.uid = 2012;
sane.ids.named.gid = 2012;
sane.ids.lpadmin.gid = 2013;
sane.ids.knot-resolver.uid = 2014;
sane.ids.knot-resolver.gid = 2014;
# found on graphical hosts
sane.ids.nm-iodine.uid = 2101; # desko/moby/lappy

View File

@@ -22,6 +22,7 @@ let
hostCfg = config.sane.hosts.by-name."${config.networking.hostName}" or null;
bindCfg = config.services.bind;
in
lib.optionalAttrs false #< XXX(2025-09-08): using kresd / knot-resolver now
{
config = lib.mkIf (!config.sane.services.hickory-dns.asSystemResolver) {
services.resolved.enable = lib.mkForce false;

View File

@@ -25,6 +25,7 @@
imports = [
./bind.nix
./hickory-dns.nix
./kresd.nix
./unbound.nix
];

View File

@@ -0,0 +1,60 @@
## config
# - <https://knot-resolver.readthedocs.io/en/stable/config-overview.html>
{ config, lib, ... }:
let
hostCfg = config.sane.hosts.by-name."${config.networking.hostName}" or null;
in
{
config = lib.mkIf (!config.sane.services.hickory-dns.asSystemResolver) {
services.resolved.enable = lib.mkForce false;
networking.nameservers = [
# be compatible with systemd-resolved
# "127.0.0.53"
# or don't be compatible with systemd-resolved, but with libc and pasta instead
# see <pkgs/by-name/sane-scripts/src/sane-vpn>
"127.0.0.1"
# enable IPv6, or don't; unbound is spammy when IPv6 is enabled but unroutable
# "::1"
];
networking.resolvconf.useLocalResolver = false; #< we manage resolvconf explicitly, above
networking.resolvconf.extraConfig = ''
# DNS serviced by `kresd` (knot-resolver) recursive resolver
name_servers='127.0.0.1'
'';
sane.persist.sys.byPath."/var/cache/knot-resolver" = {
# TODO: store the cache in private store, and restart the service once that's been unlocked?
store = "plaintext";
method = "bind";
acl.mode = "0770";
acl.user = "knot-resolver";
};
services.kresd.enable = true;
services.kresd.listenPlain = [
"127.0.0.1:53"
] ++ lib.optionals (hostCfg != null && hostCfg.wg-home.ip != null) [
# allow wireguard clients to use us as a recursive resolver (only needed for servo)
"${hostCfg.wg-home.ip}:53"
];
# TODO:
# - [x] disable DNSSEC
# - [ ] IPv4-only
# - [ ] serve tailscale records
# - [ ] persist the on-disk cache
# - [ ] integrate with dhcp-configs
services.kresd.extraConfig = ''
-- config docs: <https://www.knot-resolver.cz/documentation/stable/config-overview.html>
-- we can't guarantee that all forwarders support DNSSEC.
-- replicating my bind config, and just disabling dnssec universally
-- dnssec = false
-- trust_anchors.remove('.')
net.ipv6 = false
'';
};
}

View File

@@ -6,8 +6,9 @@ let
# nixpkgs' pam hardcodes unix_chkpwd path to the /run/wrappers one,
# but i don't want the wrapper, so undo that.
# ideally i would patch this via an overlay, but pam is in the bootstrap so that forces a full rebuild.
# see: <repo:nixos/nixpkgs:pkgs/by-name/li/linux-pam/package.nix>
postPatch = (upstream.postPatch or "") + ''
substituteInPlace modules/pam_unix/Makefile.am --replace-fail \
substituteInPlace modules/module-meson.build --replace-fail \
"/run/wrappers/bin/unix_chkpwd" "$out/bin/unix_chkpwd"
'';
});
@@ -215,8 +216,10 @@ in
# - USB keyboards: "uhci_hcd" "ehci_hcd" "ehci_pci" "ohci_hcd" "ohci_pci" "xhci_hcd" "xhci_pci" "usbhid" "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat" "hid_logitech_hidpp" "hid_logitech_dj" "hid_microsoft" "hid_cherry" "hid_corsair"
# - LVM: "dm_mod"
# - on x86 only: more keyboard stuff: "pcips2" "atkbd" "i8042"
boot.initrd.includeDefaultModules = lib.mkDefault false;
#
# however, including these modules seems relatively *harmless*,
# and it makes bringup of new systems a bit easier.
# boot.initrd.includeDefaultModules = lib.mkDefault false;
# see: <repo:nixos/nixpkgs:nixos/modules/virtualisation/nixos-containers.nix>
boot.enableContainers = lib.mkDefault false;

View File

@@ -54,6 +54,12 @@ in
key = "PageDown"
action = "ScrollPageDown"
[[keyboard.bindings]]
# disable builtin Ctrl+Shift+Space => visual selection binding
mods = "Control|Shift"
key = "Space"
action = "None"
# disable OS shortcuts which leak through...
# see sway config or sane-input-handler for more info on why these leak through
[[keyboard.bindings]]

View File

@@ -173,6 +173,7 @@ in
"nix" # needed as user package, for ~/.cache/nix persistence
# "nettools"
# "networkmanager"
"nvimpager" # needed as a user package, for config.
# "nixos-generators"
# "node2nix"
# "oathToolkit" # for oathtool
@@ -1194,6 +1195,10 @@ in
typescript-language-server.buildCost = 2;
typescript-language-server.sandbox.whitelistPwd = true;
typescript-language-server.persist.byStore.ephemeral = [
".cache/typescript"
".npm" # .npm/{_cacache,_logs}
];
tumiki-fighters.buildCost = 1;
tumiki-fighters.sandbox.whitelistAudio = true;

View File

@@ -216,6 +216,7 @@
./tcpdump.nix
./tor-browser.nix
./tuba.nix
./u-boot-pinephone-pro
./unl0kr
./uptime.nix
./v4l-utils.nix

View File

@@ -16,6 +16,7 @@
<dt><a href="https://en.wikipedia.org/wiki/Special:Search?search=%s" shortcuturl="w">Search Wikipedia
<dt><a href="https://github.com/nixos/nixpkgs/pulls?q=%s" shortcuturl="pr">Search nixpkgs PRs
<dt><a href="https://github.com/search?type=repositories&q=%s" shortcuturl="gh">Search GitHub
<dt><a href="https://kagi.com/maps/infobox?q=%s" shortcuturl="maps">Search Kagi Maps
<dt><a href="https://kagi.com/search?q=%s" shortcuturl="kagi">Search with Kagi
<dt><a href="https://lib.rs/search?q=%s" shortcuturl="librs">Search lib.rs (Rust)
<dt><a href="https://myanimelist.net/search/all?cat=all&q=%s" shortcuturl="mal">Search MyAnimeList
@@ -35,7 +36,7 @@
<dt><a href="https://www.ebay.com/sch/i.html?_sacat=0&_nkw=%s" shortcuturl="ebay">Search eBay
<dt><a href="https://www.etsy.com/search?q=%s" shortcuturl="etsy">Search Etsy
<dt><a href="https://www.etymonline.com/search?q=%s" shortcuturl="etym">Search Etymonline
<dt><a href="https://www.google.com/maps/search/%s" shortcuturl="maps">Search Google Maps
<dt><a href="https://www.google.com/maps/search/%s" shortcuturl="gmaps">Search Google Maps
<dt><a href="https://www.google.com/search?q=%s" shortcuturl="g">Search Google
<dt><a href="https://www.google.com/search?q=%s&" shortcuturl="google">Search Google
<dt><a href="https://www.google.com/search?tbm=shop&q=%s" shortcuturl="shopping">Search Google Shopping

View File

@@ -43,6 +43,10 @@ in
"knowledge/secrets/accounts"
];
# firefox learns about this package by looking in ~/.mozilla/native-messaging-hosts
fs.".mozilla/native-messaging-hosts/com.github.browserpass.native.json".symlink.target
= "${browserpass}//lib/mozilla/native-messaging-hosts/com.github.browserpass.native.json";
# TODO: env.PASSWORD_STORE_DIR only needs to be present within the browser session.
# alternative to PASSWORD_STORE_DIR:
# fs.".password-store".symlink.target = "knowledge/secrets/accounts";

View File

@@ -16,7 +16,9 @@ let
cfg.addons
);
addonSuggestedPrograms = lib.map (n: config.sane.programs."${n}") addonSuggestedProgramNames;
addonHomePaths = lib.concatMap (p: p.sandbox.extraHomePaths) (addonSuggestedPrograms ++ nativeMessagingPrograms);
addonHomePaths = lib.concatMap
(p: p.sandbox.extraHomePaths ++ builtins.attrNames p.fs)
(addonSuggestedPrograms ++ nativeMessagingPrograms);
packageUnwrapped = let
unwrapped = pkgs.firefox-unwrapped // {
@@ -29,7 +31,8 @@ let
# inherit the default librewolf.cfg
# it can be further customized via ~/.librewolf/librewolf.overrides.cfg
libName = "firefox";
inherit nativeMessagingHosts;
# XXX(2025-08-26): nativeMessagingHosts wrapping is broken! put things in ~/.mozilla/native-messaging-hosts/ instead.
# inherit nativeMessagingHosts;
nixExtensions = lib.concatMap (ext: lib.optional ext.enable ext.package) (builtins.attrValues cfg.addons);

View File

@@ -143,10 +143,24 @@ defaultPref("browser.shell.checkDefaultBrowser", false);
// disable extension updates
defaultPref("extensions.update.autoUpdateDefault", false);
defaultPref("extensions.update.enabled", false);
defaultPref("extensions.systemAddon.update.enabled", false);
// wipe the URIs used to check for updates, as a precaution.
defaultPref("extensions.update.url", "");
defaultPref("extensions.update.background.url", "");
defaultPref("extensions.systemAddon.update.url", "");
// also disable app-level auto-updates
defaultPref("app.update.auto", false);
// disable "safe browsing", in which my browser asks Google whether a site is malicious or not, for every site i visit (?)
defaultPref("browser.safebrowsing.blockedURIs.enabled", false);
defaultPref("browser.safebrowsing.downloads.enabled", false);
defaultPref("browser.safebrowsing.malware.enabled", false);
defaultPref("browser.safebrowsing.phishing.enabled", false);
// browser.engagement.sidebar-button.has-used
// browser.migration.version = 150
// allow sites to trigger desktop notifications, by default.
// i couldn't find a trivial way to plumb per-site permissions here -- probably kept in a separate db
defaultPref("permissions.default.desktop-notification", 1);

View File

@@ -26,6 +26,7 @@ in
sandbox.whitelistPwd = true;
sandbox.autodetectCliPaths = "parent"; # autodetection is necessary for git-upload-pack; "parent" so that `git mv` works
sandbox.extraHomePaths = [
".config/nvim"
# even with `whitelistPwd`, git has to crawl *up* the path -- which isn't necessarily in the sandbox -- to locate parent .git files
"dev"
"knowledge"
@@ -33,6 +34,7 @@ in
"ref"
];
sandbox.whitelistSsh = true;
fs.".config/git/config".symlink.text = mkCfg {
# top-level options documented:
# - <https://git-scm.com/docs/git-config#_variables>
@@ -54,7 +56,11 @@ in
alias.st = "status";
alias.stat = "status";
diff.noprefix = true; #< don't show a/ or b/ prefixes in diffs
commit.verbose = true; #< have `git commit` populate both status *and* diff to the editor
diff.context = 8; #< default 3 lines of context
diff.interHunkContext = 8; #< include up to this many extra lines to merge diff hunks
diff.noPrefix = true; #< don't show a/ or b/ prefixes in diffs
# difftastic docs:
# - <https://difftastic.wilfred.me.uk/git.html>
diff.tool = "difftastic";
@@ -62,12 +68,17 @@ in
"difftool \"difftastic\"".cmd = ''difft "$LOCAL" "$REMOTE"'';
# now run `git difftool` to use difftastic git
# render dates as YYYY-MM-DD HH:MM:SS +TZ
log.date = "iso";
log.date = "iso"; #< render dates as YYYY-MM-DD HH:MM:SS +TZ
log.follow = true; #< make `git log PATH` behave like `git log --follow PATH`
log.showSignature = false;
rebase.autoStash = true; #< make `git rebase FOO` behave as `git stash && git rebase FOO && git stash apply`
sendemail.annotate = "yes";
sendemail.confirm = "always";
status.short = true; #< make `git statues` behave as `git status --short`
stash.showPatch = true;
};
};

View File

@@ -4,17 +4,20 @@
# TODO: migrate nixpkgs mimeo to be `buildPythonPackage` to make it importable here.
# see <doc/languages-frameworks/python.section.md>
import argparse
import subprocess
import sys
desktop = sys.argv[1]
opener_args = sys.argv[2:]
parser = argparse.ArgumentParser(description="open some file or URL with a specific .desktop handler")
parser.add_argument("desktop", help="name of .desktop file")
parser.add_argument("opener_args", nargs="+")
args = parser.parse_args()
desktop_fields=subprocess.check_output([
"mimeo",
"--desk2field",
"Exec",
desktop
args.desktop
]).decode('utf-8')
# print(f"fields: {desktop_fields!r}")
@@ -31,9 +34,9 @@ desktop_argv = [f.strip() for f in desktop_exec.split(' ') if f.strip()]
substituted_argv = []
for arg in desktop_argv:
if arg == '%U':
substituted_argv += opener_args
substituted_argv += args.opener_args
elif arg == '%u':
substituted_argv += opener_args[:1]
substituted_argv += args.opener_args[:1]
else:
substituted_argv += [arg]

View File

@@ -36,7 +36,8 @@
sandbox.extraPaths = [
"/boot"
"/mnt/desko"
"/mnt/lappy"
"/mnt/flowy"
# "/mnt/lappy"
"/mnt/moby"
"/mnt/servo"
# "nix"

View File

@@ -67,12 +67,12 @@ in
viAlias = true;
vimAlias = true;
plugins = plugin-packages;
customRC = ''
${builtins.readFile ./vimrc}
# customRC = ''
# ${builtins.readFile ./vimrc}
""""" PLUGIN CONFIG
${plugin-configs}
'';
# """"" PLUGIN CONFIG
# ${plugin-configs}
# '';
};
neovim-unwrapped' = with pkgs; neovim-unwrapped.overrideAttrs (upstream: {
# fix cross compilation:
@@ -122,14 +122,22 @@ in
# due to <https://github.com/NixOS/nixpkgs/pull/344541>
rubyEnv = null;
withRuby = false;
wrapRc = false; #< don't force VIMINIT env var
postBuild = lib.replaceStrings [ "if ! $out/bin/nvim-wrapper " ] [ "if false " ] base.postBuild;
});
fs.".config/nvim/init.vim".symlink.text = ''
${builtins.readFile ./vimrc}
""""" PLUGIN CONFIG
${plugin-configs}
'';
# private because there could be sensitive things in the swap
persist.byStore.private = [ ".cache/vim-swap" ];
env.EDITOR = "vim";
# git claims it should use EDITOR, but it doesn't!
env.GIT_EDITOR = "vim";
# git falls back to EDITOR if GIT_EDITOR is unspecified
# env.GIT_EDITOR = "vim";
mime.priority = 200; # default=100 => yield to other, more specialized applications
mime.associations."application/schema+json" = "nvim.desktop";
mime.associations."plain/text" = "nvim.desktop";

View File

@@ -1,3 +1,6 @@
" ENV VARS
" VIMINIT=/path/to/init.vim to use a different vimrc
" let the terminal handle mouse events, that way i get OS-level ctrl+shift+c/etc
" this used to be default, until <https://github.com/neovim/neovim/pull/19290>
set mouse=

View File

@@ -1,19 +1,32 @@
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.nvimpager;
in
{
sane.programs.nvimpager = {
packageUnwrapped = (pkgs.nvimpager.override {
neovim = config.sane.programs.neovim.packageUnwrapped;
}).overrideAttrs {
}).overrideAttrs (upstream: {
# force nvimpager to use my vim config.
# by default it loads ~/.config/nvimpager/init.vim instead.
# could instead symlink the latter, but sandboxing issues.
postPatch = (upstream.postPatch or "") + ''
substituteInPlace nvimpager \
--replace-fail 'args=(' 'args=(-u ~/.config/nvim/init.vim '
'';
# check phase fails, something to do with me enabling plugins not expected by the tester
doCheck = false;
};
});
suggestedPrograms = [ "neovim" ];
sandbox.extraHomePaths = [
".config/nvim"
];
sandbox.whitelistWayland = true; # for system clipboard integration
env.MANPAGER = "nvimpager";
# env.PAGER = "nvimpager";
env.PAGER = "nvimpager";
# `man 2 select` will have `man` render the manpage to plain text, then pipe it into vim for syntax highlighting.
# force MANWIDTH=999 to make `man` not hard-wrap any lines, and instead let vim soft-wrap lines.
# that allows the document to be responsive to screen-size/windowing changes.
@@ -21,4 +34,8 @@
env.MANWIDTH = "999";
env.MANROFFOPT = "-c";
};
sane.programs.man-db.sandbox.extraHomePaths = lib.mkIf cfg.enabled [
".config/nvim"
];
}

View File

@@ -11,6 +11,10 @@ let
type = lib.types.bool;
inherit default description;
};
i3ipc = pkgs.python3Packages.i3ipc.overridePythonAttrs {
# XXX(2025-08-25): tests are broken; remove once fixed
doCheck = false;
};
playerctl = pkgs.playerctl.overrideAttrs (upstream: {
patches = (upstream.patches or []) ++ [
(pkgs.fetchpatch {
@@ -108,6 +112,9 @@ in
packageUnwrapped = (pkgs.nwg-panel.override {
inherit playerctl;
python3Packages = pkgs.python3Packages // {
inherit i3ipc;
};
}).overrideAttrs (base: {
# patches = (base.patches or []) ++ lib.optionals (!cfg.config.mediaPrevNext) [
# ./playerctl-no-prev-next.diff

View File

@@ -24,7 +24,7 @@
# - <https://gitlab.com/DamienCassou/rofi-pulse-select>
{ pkgs, ... }:
let
rofi-unwrapped = pkgs.rofi-wayland-unwrapped.overrideAttrs (upstream: {
rofi-unwrapped = pkgs.rofi-unwrapped.overrideAttrs (upstream: {
patches = (upstream.patches or []) ++ [
(pkgs.fetchpatch {
# so that i can open applications via the xdg-desktop-portal instead of by having rofi launch them directly.
@@ -81,7 +81,7 @@ in
sane.programs.rofi = {
# 2024/02/26: wayland is only supported by the fork: <https://github.com/lbonn/rofi>.
# it's actively maintained though, and more of an overlay than a true fork.
packageUnwrapped = pkgs.rofi-wayland.override {
packageUnwrapped = pkgs.rofi.override {
inherit rofi-unwrapped;
plugins = with pkgs; [
# rofi-calc # not compatible with my rofi <https://github.com/svenstaro/rofi-calc>
@@ -171,7 +171,7 @@ in
srcRoot = ./.;
pkgs = {
inherit (pkgs) gnused wtype;
rofi-wayland = pkgs.rofi-wayland.override {
rofi = pkgs.rofi.override {
inherit rofi-unwrapped;
};
};

View File

@@ -1,14 +1,42 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p bash -p gnused -p rofi-wayland -p wtype
#!nix-shell -i bash -p bash -p gnused -p rofi -p wtype
# "bookmarking"/snippets inspired by Luke Smith:
# - <https://www.youtube.com/watch?v=d_11QaTlf1I>
# rofi flags (see: `man rofi-dmenu`):
# `-i`: case insensitive filtering
# `-sync -ellipsize-mode middle`: for lengthy entries, replace the *middle* with an ellipsis instead of the end
# requires rofi 1.7.6, and `-sync`, wich must come *before* the `-dmenu` flag
cat ~/.config/rofi-snippets/public.txt ~/.config/rofi-snippets/private.txt | \
rofi -sync -ellipsize-mode middle -dmenu -i | \
sed -z -e 's/ *#.*$//' -e 's/\n$//' | \
wtype -
usage() {
echo "rofi-snippets:"
echo " launch a rofi graphical menu to choose from several preset snippets/bookmarks."
echo " the selected snippet is then sent to the active window as if typed by the user."
}
parseArgs() {
while [ $# -ne 0 ]; do
local arg="$1"
shift
case "$arg" in
(--help)
usage
exit 0
;;
(*)
usage
exit 1
;;
esac
done
}
main() {
# rofi flags (see: `man rofi-dmenu`):
# `-i`: case insensitive filtering
# `-sync -ellipsize-mode middle`: for lengthy entries, replace the *middle* with an ellipsis instead of the end
# requires rofi 1.7.6, and `-sync`, wich must come *before* the `-dmenu` flag
cat ~/.config/rofi-snippets/public.txt ~/.config/rofi-snippets/private.txt | \
rofi -sync -ellipsize-mode middle -dmenu -i | \
sed -z -e 's/ *#.*$//' -e 's/\n$//' | \
wtype -
}
parseArgs "$@"
main

View File

@@ -14,8 +14,8 @@ in
"sane-scripts.bt-show"
];
"sane-scripts.dev" = declPackageSet [
"sane-scripts.clone"
"sane-scripts.dev-cargo-loop"
# "sane-scripts.clone" #< TODO: make `sane_clone` a shell alias
# "sane-scripts.dev-cargo-loop"
"sane-scripts.profile"
];
"sane-scripts.cli" = declPackageSet [

View File

@@ -23,6 +23,7 @@
'';
});
sandbox.extraHomePaths = [
".config/nvim"
".config/sops"
"nixos"
# TODO: sops should only need access to knowledge/secrets,

View File

@@ -10,11 +10,37 @@ usage() {
echo "- up <service>"
echo "- down <service>"
echo "- toggle <service>"
exit 1
}
action="$1"
service="$2"
action=
service=
parseArgs() {
while [ "$#" -ne 0 ]; do
local arg=$1
shift
case $arg in
(--help)
usage
exit 0
;;
(*)
if [ -z "$action" ]; then
action=$arg
elif [ -z "$service" ]; then
service=$arg
else
usage
exit 1
fi
;;
esac
done
if [ -z "$action" ] || [ -z "$service" ]; then
usage
exit 1
fi
}
log() {
if [ -n "$SWAYNC_DEBUG" ]; then
@@ -34,28 +60,34 @@ stopService() {
systemctl stop "$service"
}
case "$action" in
(print)
checkActive
;;
(toggle)
case "$(checkActive)" in
false)
startService
;;
true)
stopService
;;
esac
;;
# these aren't needed by swaync; just handy for testing/debugging
(up)
startService
;;
(down)
stopService
;;
(*)
usage
;;
esac
main() {
case "$action" in
(print)
checkActive
;;
(toggle)
case "$(checkActive)" in
false)
startService
;;
true)
stopService
;;
esac
;;
# these aren't needed by swaync; just handy for testing/debugging
(up)
startService
;;
(down)
stopService
;;
(*)
usage
exit 1
;;
esac
}
parseArgs "$@"
main

View File

@@ -0,0 +1,21 @@
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.u-boot-pinephone-pro;
in
{
sane.programs.u-boot-pinephone-pro = {
packageUnwrapped = pkgs.runCommandNoCC "u-boot-pinephone-pro-program" {
preferLocalBuild = true;
} ''
install -Dm644 ${pkgs.u-boot-pinephone-pro}/idbloader.img $out/share/boot/idbloader.img
install -Dm644 ${pkgs.u-boot-pinephone-pro}/u-boot.itb $out/share/boot/u-boot.itb
install -Dm755 ${./install-u-boot} $out/bin/install-u-boot
# ln -sv $out/bin/install-u-boot $out/share/boot/install-u-boot
'';
sandbox.autodetectCliPaths = "existingFile";
};
environment.pathsToLink = lib.mkIf cfg.enabled [
"/share/boot"
];
}

View File

@@ -0,0 +1,25 @@
#!/bin/sh
set -eu
drive=$1
if ! [[ -e "$drive" ]]; then
echo "usage: install-u-boot /dev/sdX"
exit 1
fi
bootfiles_dir=
for d in ${XDG_DATA_DIRS//:/ }; do
if [[ -e "$d/boot/idbloader.img" ]] && [[ -e "$d/boot/u-boot.itb" ]]; then
bootfiles_dir=$d/boot
fi
done
if [[ -z "$bootfiles_dir" ]]; then
echo "boot/{idbloader.img,u-boot.itb} not found on XDG_DATA_DIRS"
exit 1
fi
set -x
dd if="$bootfiles_dir/idbloader.img" of="$drive" bs=512 seek=64 conv=notrunc,sync oflag=direct status=progress
dd if="$bootfiles_dir/u-boot.itb" of="$drive" bs=512 seek=16384 conv=notrunc,sync oflag=direct status=progress

View File

@@ -65,7 +65,8 @@
sandbox.extraPaths = [
"/boot"
"/mnt/desko"
"/mnt/lappy"
"/mnt/flowy"
# "/mnt/lappy"
"/mnt/moby"
"/mnt/servo"
# "nix"

View File

@@ -73,7 +73,6 @@ in
"mx-sanebot-env".owner = config.users.users.colin.name;
"rsync-net-env".owner = config.users.users.colin.name;
"rsync-net-id_ed25519".owner = config.users.users.colin.name;
"tailscale-work-zones-bind.conf".owner = "named";
"transmission_passwd".owner = config.users.users.colin.name;
}
];

View File

@@ -116,9 +116,9 @@ in
# see: `man logind.conf`
# dont shutdown when power button is short-pressed (commonly done an accident, or by cats).
# but do on long-press: useful to gracefully power-off server.
services.logind.powerKey = "lock";
services.logind.powerKeyLongPress = "poweroff";
services.logind.lidSwitch = "lock";
services.logind.settings.Login.HandlePowerKey = "lock";
services.logind.settings.Login.HandlePowerKeyLongPress = "poweroff";
services.logind.settings.Login.HandleLidSwitch = "lock";
# under logind, 'uaccess' tag would grant the logged in user access to a device.
# outside logind, map uaccess tag -> plugdev group to grant that access.
services.udev.extraRules = ''

View File

@@ -11,6 +11,7 @@ in
config = lib.mkIf cfg.enable {
# disable the following non-essential programs which fail to cross compile
sane.programs.bash-language-server.enableFor = { system = false; user.colin = false; }; # bash neovim LSP: doesn't cross compile (2025-01-05; blocked by ShellCheck)
sane.programs.cargo.enableFor = { system = false; user.colin = false; }; #< does not cross compile (2025-08-25)
sane.programs.fcitx5.enableFor.user.colin = false; #< does not cross compile (2025-01-05; blocked by qtsvg)
sane.programs.firefox.config.addons.browserpass-extension.enable = false; #< does not cross compile
sane.programs.lua-language-server.enableFor = { system = false; user.colin = false; }; # lua neovim LSP: doesn't cross compile (2025-01-06)
@@ -19,23 +20,16 @@ in
sane.programs.nix-tree.enableFor = { system = false; user.colin = false; }; #< does not cross compile (2025-01-05; blocked by vty)
sane.programs.pyright.enableFor = { system = false; user.colin = false; }; #< python neovim LSP: doesn't cross compile (2025-01-05; unblocked)
sane.programs.typescript-language-server.enableFor = { system = false; user.colin = false; }; #< doesn't cross compile (2025-07-18; via `moreutils`)
sane.programs.vulkan-tools.enableFor = { system = false; user.colin = false; }; #< does not cross compile (2025-02-08)
boot.kernelPatches = [
boot.kernelPatches = lib.optionals (!pkgs.stdenv.hostPlatform.linux-kernel.preferBuiltin) [
{
# TODO: upstream into nixpkgs. <repo:nixos/nixpkgs:pkgs/os-specific/linux/kernel/common-config.nix>
name = "fix-module-builtin-mismatch";
patch = null;
structuredExtraConfig = with lib.kernel; {
# nixpkgs specifies `SUN8I_DE2_CCU = yes`, but that in turn requires `SUNXI_CCU = yes` and NOT `= module`
# symptom: config fails to eval
# symptom: Kconfig build fails
SUNXI_CCU = yes;
# nixpkgs specifies `DRM = yes`, which causes `DRM_PANEL=y`.
# in <repo:kernel.org/linux:include/drm/drm_panel.h> they branch based on if `CONFIG_BACKLIGHT_DEVICE` is a *builtin*,
# hence we need to build it as a builtin to actually have a backlight!
# same logic happens with nixpkgs `DRM_FBDEV_EMULATION = yes` => implies `CONFIG_DRM_KMS_HELPER=y`
# and <repo:kernel.org/linux:include/drm/display/drm_dp_helper.h>
BACKLIGHT_CLASS_DEVICE = yes;
};
}
];

View File

@@ -40,16 +40,31 @@ in
# boot.kernelPackages = pkgs.linuxPackagesFor myCustomKernel;
# boot.initrd.extraFiles."/lib".source = "${config.system.modulesTree}/lib";
# docs (pinephone specific; tow-boot instead of u-boot but close enough): <https://github.com/Tow-Boot/Tow-Boot/tree/development/boards/pine64-pinephoneA64>
# Pinephone Pro bootloader locations: <https://wiki.pine64.org/wiki/RK3399_boot_sequence#U-Boot_boot_sequence>
# we need space in the GPT header to place u-boot.
# only actually need 1 MB, but better to over-allocate than under-allocate
sane.image.extraGPTPadding = 16 * 1024 * 1024;
sane.image.firstPartGap = 0;
# N.B.: the original PP expected idbloader to be at block 16.
# PPP expects idbloader to be at block 64.
# GPT header ends at block 34, which means PPP can use an ordinary partition table
# just with the first partition starting at e.g. 16 MiB instead of block 34.
# sane.image.extraGPTPadding = 16 * 1024 * 1024 - 34 * 512;
# sane.image.firstPartGap = 0;
sane.image.installBootloader = ''
dd if=${pkgs.u-boot-pinephone-pro}/idbloader.img of=$out bs=512 seek=64 oflag=direct conv=sync
dd if=${pkgs.u-boot-pinephone-pro}/u-boot.itb of=$out bs=512 seek=16384 oflag=direct conv=sync
uboot_itb_bytes=$(stat --printf="%s" ${pkgs.u-boot-pinephone-pro}/u-boot.itb)
uboot_ends=$(( $uboot_itb_bytes + 16384 * 512))
gap_ends=${builtins.toString config.sane.image.firstPartGap}
if ! (( $uboot_ends <= $gap_ends )); then
echo 'firstPartGap is too small to fit all of u-boot!'
false
fi
dd if=${pkgs.u-boot-pinephone-pro}/idbloader.img of=$out bs=512 seek=64 conv=notrunc
dd if=${pkgs.u-boot-pinephone-pro}/u-boot.itb of=$out bs=512 seek=16384 conv=notrunc
'';
sane.programs.sysadminUtils.suggestedPrograms = [
"u-boot-pinephone-pro"
];
sane.programs.alsa-ucm-conf.suggestedPrograms = [
"pine64-alsa-ucm" # upstreaming: https://github.com/alsa-project/alsa-ucm-conf/pull/375
];
@@ -88,6 +103,10 @@ in
}
];
#v N.B.: deviceTree.name is plumbed through /boot/loader/entries/.
#v if removed, systemd-boot will still (likely) boot, but DTB items known to the kernel
#v and not to the platform firmware (u-boot) will be missing (e.g. rk818/battery monitoring).
hardware.deviceTree.name = "rockchip/rk3399-pinephone-pro.dtb";
hardware.deviceTree.overlays = [
{
name = "rk3399-pinephone-pro-battery";
@@ -109,9 +128,15 @@ in
name = "rk3399-pinephone-pro-modem";
dtsFile = ./rk3399-pinephone-pro-modem.dtso;
}
# {
# name = "rk3399-pinephone-pro-sound";
# dtsFile = ./rk3399-pinephone-pro-sound.dtso;
# }
{
name = "rk3399-pinephone-pro-sound";
dtsFile = ./rk3399-pinephone-pro-sound.dtso;
# the complete sound dtso above works in extlinux, but fails under systemd-boot.
# this simpler sound config may be helpful in debugging.
name = "rk3399-pinephone-pro-sound-minimal";
dtsFile = ./rk3399-pinephone-pro-sound-minimal.dtso;
}
];
@@ -209,9 +234,12 @@ in
# from <repo:nixos/nixpkgs:nixos/modules/system/boot/kernel.nix> AKA pkgs.aggregateModules
# but configured to **ignore collisions**
system.modulesTree = lib.mkForce [(
(pkgs.aggregateModules
( config.boot.extraModulePackages ++ [ config.boot.kernelPackages.kernel ])
).overrideAttrs {
(pkgs.aggregateModules (
config.boot.extraModulePackages ++ [
(lib.getOutput "modules" config.boot.kernelPackages.kernel)
]
)).overrideAttrs {
name = "kernel-modules-merged-sane";
# earlier items override the contents of later items
ignoreCollisions = true;
# checkCollisionContents = false;

View File

@@ -0,0 +1,303 @@
// heavily based on megi's source tree.
// schematics for reference: <https://files.pine64.org/doc/PinePhonePro/PinephonePro-Schematic-V1.0-20211127.pdf>
// Realtek ALC5640 datasheet: <https://www.alldatasheet.com/datasheet-pdf/download/1132334/REALTEK/RT5640.html>
//
// ## DIGITAL RK3399 -> ALC5640 INTERFACE
// [RK3399]GPIO4_A0/I2S_CLK_d -> I2S_CLK -> MCLK[ALC5640]
// [RK3399]GPIO3_D0/I2S0_SCLK_d -> I2S0_SCLK -> BCLK1[ALC5640]
// [RK3399]GPIO3_D1/I2S0_LRCK_RX_d -> I2S0_LRCK_RX -> LRCLK1[ALC5640]
// [RK3399]GPIO3_D2/I2S0_LRCK_TX_d -> I2S0_LRCK_TX -> LRCLK1[ALC5640]
// (yes, I2S0_LRCK_TX and I2S0_LRCK_RX are both bridged to LRCLK1, but with separate resistors probably so they don't short)
// [RK3399]GPIO3_D7/I2S0_SDO0_d -> I2S0_SDO0 -> DACDAT1[ALC5640]
// [RK3399]GPIO3_D3/I2S0_SDI0_d <- I2S0_SDI0 <- ADCDAT1[ALC5640]
//
// the ALC5640 connects also to bluetooth, and the chip as a whole has
// - I2C1_SCL
// - I2C1_SDA
// i believe I2C1 is for control data, whereas I2S is strictly for the audio stream.
//
// ## ALC5640 <-> PHY interface
// [ALC5640]HPO_L -> HPOUTL -> NO2[UM4717] -> COM2[UM4717] -> HPO_L -> JA-3618-011 (jack, "EARPHONE")
// +----> INN[AW8737SCSR] -> SPK+/SPK-
// so, the codec's HPO_L output is sent to the SW-6 MUX (user-exposed, controls whether jack is audio or serial)
// AND it's sent to AW8737SCSR which *may* send it to the speaker.
// i think audio is *unconditionally* routed to the headphones, then.
// it's impossible to have headphones connected, and play *only* to the speaker.
// and it's up to the system to disable AW8737SCSR when the headphones are detected.
// [ALC5640]HPO_R -> HPOUTR [... same as above]
//
// [ALC5640]SPO_LP -> EAROUTP -> 1[J7202/earphone]
// [ALC5640]SPO_LN -> EAROUTN -> 2[J7202/earphone]
//
// ## amplifiers
// [RK3399]B3 -> SPK_CTL_H -> SHDN[AW8737SCSR]
//
// ## Headphone detection (RK3399)
// [RK3399]GPIO4_D4_d -> HP_DET_H -> HP_DET (circuit)
// - HP_DET is pulled to 3V0 when jack is *unplugged*, pulled to HPO_L when jack is plugged.
//
// ## REGULATORS
// - AVDD18_REF -> ALC5640
// - VCCA3V0_CODEC -> ALC5640
// - VCC_SPK -> ALC5640
// - VDD_CODEC -> ALC5640
// - AVDD18_CODEC -> ALC5640
// - VCCA1V8_CODEC -> ALC5640
//
// mainline offers at least these regulators:
// - vcc_sys (regulator-always-on)
// - vcc3v3_sys
// - vcca1v8_s3 (regulator-always-on)
// - vcc1v8_codec (pinctrl-0 = <&vcc1v8_codec_en>)
// - pinctrl routed to &{/pinctrl/sound}
// - nothing in-kernel references vcc1v8_codec_en ...
// megi changed this to regulator-always-on, regulator-boot-on, though
//
//
// note that the I2S pins described in the schematic above match ths `i2s0` defined in `rk3399-base.dtsi`,
// so in general the names should map 1:1
//
// the following in-kernel resources exist:
// - <sound/soc/codecs/rt5640.c>
// driver which is (i think?) loaded by `compatible = "realtek,rt5640"`
// - <Documentation/devicetree/bindings/sound/simple-card.yaml>
// for `compatible = "simple-audio-card"`
// - <arch/arm64/boot/dts/rockchip/rk3399-firefly.dts>
// other RK3399 device with audio configured
// - <Documentation/devicetree/bindings/mfd/rockchip,rk818.yaml>
// regulator definition
// - <Documentation/devicetree/bindings/sound/rockchip-i2s.yaml>
// I2S audio definition
// for symbols like SCLK_I2S_8CH_OUT
#include <dt-bindings/clock/rk3399-cru.h>
// for RK_PD4, other pins
#include <dt-bindings/pinctrl/rockchip.h>
// for GPIO_ACTIVE_LOW
#include <dt-bindings/gpio/gpio.h>
/dts-v1/;
/plugin/;
/ {
/* ensure this overlay applies only to the correct board */
compatible = "pine64,pinephone-pro";
};
&{/} {
// megi says:
// > in1 - digital mic daughhterboard
// > in2 - headset mic
// > in3 - modem output (muxed with mono)
// > spol - earphone
// > hpo - heaphones
// > lout - modem input
// > spaker - amp enabled by SPK_CTL_H
//
// > mclk - GPIO4_A0/I2S_CLK
sound {
compatible = "simple-audio-card";
simple-audio-card,name = "PinePhonePro";
simple-audio-card,format = "i2s";
// frequency scale between stream rate and codec mclk.
// megi assigned 24 MHz to i2c1, which i think means the sample rate of the codec would then be 93.75 kHz (so 46.875 kHz per channel?).
// mainline i2c1 frequence is 200 MHz.
// the device tree makes this tunable, but ALC5640 docs are written as if it's *always* 256.
// oh, perhaps i'm just *informing* the `simple-audio-card` of this fact,
// and it doesn't configure anything on the rt5640, but rather uses this to calculate its own sample rate.
simple-audio-card,mclk-fs = <256>;
// see schematic GPIO4_D4_d -> link HP_DET
// simple-audio-card,hp-det-gpio = <&gpio4 RK_PD4 GPIO_ACTIVE_LOW>;
// <sink>, <source>
// - these names i *think* come from <sound/soc/codecs/rt5640.c>,
// or i'm free to define new names for virtual nodes.
//
// HPOL/HPOR -> user kill switch SW1-6 -> headphone jack
// ALSO sent to the speaker amp
// SPOLP/SPOLN -> earphone (with only passives in between)
//
// naming ("Internal Earpiece") is chosen to be consistent with OG pinephone
// "Speaker Amp {INL,INR,OUTL,OUTR}" actually come from simple-audio-amplifier.
// - the speaker_amp dt node specifies "Speaker Amp" as its name-prefix,
// and <sound/soc/codecs/simple-amplifier.c> (i.e. simple-audio-amplifier) uses the INL/OUTL naming
//
// there may be some interplay between the widgets/routing defined here and the alsa configs though.
simple-audio-card,routing =
// each entry is $source $sink
"Headphones", "HPOL",
"Headphones", "HPOR",
// Internal Speaker would normally go through the external amp.
// but i'm omitting the amp, so bridge it straight to HPOL/HPOR.
// Internal Speaker has to be defined here so that we can expose
// it as a pin-switch, as expected by the alsa UCM profile.
"Internal Speaker", "HPOL",
"Internal Speaker", "HPOR",
"DMIC1", "Internal Microphone";
// user-facing controls. e.g. when user selects "Headphone", activate the "Headphone Jack" path.
simple-audio-card,widgets =
"Microphone", "Internal Microphone",
"Headphone", "Headphones",
"Speaker", "Internal Speaker";
//v switch is required, for alsa UCM init
//v `amixer -c 0 cset name='Internal Speaker Switch' off`
//v if any one step of the profile EnableSequence errors, the rest of the sequence is aborted => no audio
simple-audio-card,pin-switches = "Internal Speaker";
// omitting hp-det-gpios enables ALL profiles to be switched manually (under Configuration tab of e.g. wiremix/pavucontrol)
// simple-audio-card,hp-det-gpios = <&gpio4 RK_PD4 GPIO_ACTIVE_LOW>;
//
// pinctrl-names = "default";
// pinctrl-0 = <&hp_det_pin>;
simple-audio-card,cpu {
sound-dai = <&i2s0>;
};
simple-audio-card,codec {
sound-dai = <&rt5640>;
// system-clock-frequency = <12288000>;
};
};
};
// declared in rk3399-pinephone-pro.dts
// TODO: spot check the routing on these... why are there two 1v8's in the device tree?
&vcc1v8_codec {
// enable power to the regulator, always
regulator-always-on;
regulator-boot-on;
};
&vcca1v8_codec {
// enable power to the regulator, always
regulator-always-on;
regulator-boot-on;
};
&vcca3v0_codec {
// enable power to the regulator, always
regulator-always-on;
regulator-boot-on;
};
// declared in rk3399-base.dtsi, as `i2c1: i2c@ff110000 { ... }`
&i2c1 {
status = "okay";
// ALC5640 datasheet specifies max i2c rate of 400kHz
// - max rise time: 300ns
// - max fall time: 300ns
clock-frequency = <400000>;
// from rk3399-firefly.dts
// note that i2c3 in pinephone-pro is defined as 450/15 rise/fall
// i2c-scl-rising-time-ns = <300>;
// i2c-scl-falling-time-ns = <15>;
// /i2c1 declares these things, but they aren't seen from the context of this overlay it seems
// this problem is actually mentioned here: <https://pine64.org/documentation/ROCKPro64/Software/Device_Tree_Overlays_on_Mainline/>
// #address-cells = <1>;
// #size-cells = <0>;
rt5640: rt5640@1c {
compatible = "realtek,rt5640";
// reg = address of the device (a.k.a register?). Documentation/devicetree/bindings/sound/rt5640.txt uses 0x1c in their example; megi uses it too.
reg = <0x1c>;
// `cru` = Clocks and Reset unit. declared in rk3399-base.dtsi as `cru: clock-controller@ff760000 { ... }`
// the names of the clocks this cru provides are hardcoded in <dt-bindings/clock/rk3399-cru.h>
// i don't know what "8CH_OUT" means here, but this is the *only* `I2S_*` clock, so it must be right?
// i think this feeds all the i2s clocks (via mux)
clocks = <&cru SCLK_I2S_8CH_OUT>;
clock-names = "mclk";
// assigned-clocks = <&cru SCLK_I2S0_8CH>; //< TODO
// assigned-clock-rates = <11289599>; //< TODO
// XXX: this differs from megi's kernel (which declares in3 as differential).
// the schematics show `IN2P` / `IN2N` as being differential inputs though, with no mention of "in3"
// realtek,in2-differential; //< IN2 = Headset Microphone
// realtek,in3-differential;
// megi has lout-differential, but schematic shows LOUT{R,L} => 4G_IN_{R,L} i.e. stereo.
// sounds like the *modem* misconfigures its end to be differential, though
// realtek,lout-differential;
// codec has ldo1-en connected to 1.8V via pull-up, i.e. permanently enabled, no GPIO?
// realtek,ldo1-en-gpios = VCCA1V8_CODEC;
// PDM_SDI1_MIC -> IN1P/DMIC1_DAT
realtek,dmic1-data-pin = <1>;
// realtek,dmic2-data-pin = <0>; //< presumed default
// [RK3399]GPIO4_D4_d -> HP_DET_H -> HP_DET
// i think jack detection is handled by the SoC, not by the codec chip.
// it appears that HP_DET is pulled to 3V0 when jack is unplugged,
// and pulled to HPO_L when the jack is plugged.
// realtek,jack-detect-source = <>;
//
//`jack-detect-source`:
// - 0: Jack Detect function is not used
// - 1: Use GPIO1 for jack-detect
// - 2: Use JD1_IN4P for jack-detect
// - 3: Use JD2_IN4N for jack-detect
// - 4: Use GPIO2 for jack-detect
// - 5: Use GPIO3 for jack-detect
// - 6: Use GPIO4 for jack-detect
// related: `realtek,jack-detect-not-inverted` (boolean property)
// realtek,jack-detect-source = <>;
// see: <https://www.kernel.org/doc/html/v6.11/sound/soc/dai.html>
// DAI = Digital Audio Interface, in this case I2S, a 4-wire protocol with TX, RX, BCLK, LRCLK.
// i think there's only one DAI on the board (the i2s DAI), and this is telling the driver the index of that DAI?
#sound-dai-cells = <0>;
// TODO: i think all of these can be removed? (they're specific to *megi's* rt5640 driver, and not mainline?)
// assigned-clocks = <&cru SCLK_I2S0_8CH>;
// assigned-clock-rates = <11289599>; // 44100 * 256
// // rt5645 describes avdd-supply, cpvdd-supply, but rt5640 does not.
// // <Documentation/devicetree/bindings/sound/realtek,rt5645.yaml>
// avdd-supply = <&vcca3v0_codec>;
// cpvdd-supply = <&vcca3v0_codec>;
// spkvdd-supply = <&vcc5v0_sys>;
// dbvdd-supply = <&vcca1v8_codec>;
};
};
// defined (as disabled) in rk3399-base.dtsi:
// - i2s0: i2s@ff880000
// compatible = "rockchip,rk3399-i2s", "rockchip,rk3066-i2s"
&i2s0 {
rockchip,playback-channels = <2>;
// builtin mic on IN1, headphone mic on IN2
rockchip,capture-channels = <2>;
// i2s0_2ch_bus is [RK3399]PD0,PD1,PD2,PD3,PD7,PA0
// maps to I2S0_SCLK,I2S0_LRCK_RX,I2S0_LRCK_TX,I2S0_SDI0,I2S0_SDO0,I2S_CLK
// 8ch_bus adds PD4,PD5,PD6
// maps to NC,NC,TP8
// pinctrl-0 = <&i2s0_2ch_bus>;
// upstream sets this to the following:
// pinctrl-0 = <&i2s0_8ch_bus>;
// pinctrl-1 = <&i2s0_8ch_bus_bclk_off>;
status = "okay";
};
// &pinctrl {
// // sound {
// // hp_det_pin: hp-det-pin {
// // // schematics specify a 100k pull-up to VCC_3V0 (defined as regulator-always-on in rk3399-pinephone-pro.dts)
// // rockchip,pins = <4 RK_PD4 RK_FUNC_GPIO &pcfg_pull_none>;
// // };
// // };
// uart2c {
// uart2c_xfer: uart2c-xfer {
// // headphone out interferes with debug UART2.
// // it seems that the amp tries to output negative signals, through the UM4717 MUX.
// // that MUX chooses between UART and audio, to route to the headphone jack.
// // unfortunately it's not designed to handle below-ground signals.
// // long-term fix is to mix a DC offset into the amp output.
// // until then, pull RX to *ground* instead of supply, so that it's less affected by (apparent) shorting with the HPOL channel.
// // or even just leave it without any pull resistor -- so far that seems to be pretty stable?
// // note that this only fixes the spurious UART/spurious SYSRQ errors,
// // it does not address the distortion likely caused by this.
// rockchip,pins =
// <4 RK_PC3 1 &pcfg_pull_none>, // UART2DBG_RX
// <4 RK_PC4 1 &pcfg_pull_none>; // UART2DBG_TX
// };
// };
// };
// TODO: what are `io_domains`?

View File

@@ -125,6 +125,7 @@
//
// there may be some interplay between the widgets/routing defined here and the alsa configs though.
simple-audio-card,routing =
// each entry is $source $sink
"Headphones", "HPOL",
"Headphones", "HPOR",
// SPO_LP/SPO_LN -> EAROUTP -> EARPHONE (labeled in the "Mic" section of schematic)
@@ -169,6 +170,9 @@
};
speaker_amp: audio-amplifier {
// vcc5v0_sys is supplied by the modem;
// speaker amplifier is therefore unpowered if modem is off
VCC-supply = <&vcc5v0_sys>;
compatible = "simple-audio-amplifier";
pinctrl-names = "default";
pinctrl-0 = <&spk_en>;

View File

@@ -104,7 +104,7 @@ in
# docs (pinephone specific; tow-boot instead of u-boot but close enough): <https://github.com/Tow-Boot/Tow-Boot/tree/development/boards/pine64-pinephoneA64>
# we need space in the GPT header to place u-boot.
# only actually need 1 MB, but better to over-allocate than under-allocate
sane.image.extraGPTPadding = 16 * 1024 * 1024;
sane.image.extraGPTPadding = 16 * 1024 * 1024 - 34 * 512;
sane.image.firstPartGap = 0;
sane.image.installBootloader = ''
dd if=${pkgs.u-boot-pinephone}/u-boot-sunxi-with-spl.bin of=$out bs=1024 seek=8 conv=notrunc

View File

@@ -7,6 +7,18 @@ in
sane.hal.rpi-400.enable = lib.mkEnableOption "Raspberry Pi 400 hardware support";
};
config = lib.mkIf cfg.enable {
sane.image.extraBootFiles = [ pkgs.bootpart-u-boot-rpi-aarch64 ];
sane.image.extraBootFiles = [
# rpi bootrom -> edk2 -> systemd-boot
# edk2 exists here to provide the base UEFI environment which systemd-boot expects
pkgs.bootpart-edk2-rpi
];
#v used by systemd-boot
hardware.deviceTree.name = "broadcom/bcm2711-rpi-400.dtb";
# XXX(2025-08-31): stock kernel boots to initrd, but appears (?) to fail to mount root:
# mmc0: invalid bus width
# mmc0: error -22 whilst initialising SD card
# linux_rpi4 seems to do the same...
# boot.kernelPackages = pkgs.linuxKernel.packages.linux_rpi4;
};
}

View File

@@ -9,26 +9,26 @@ in
};
};
config = lib.mkIf cfg.enable {
boot.initrd.availableKernelModules = [
"xhci_pci" "ahci" "sd_mod" "sdhci_pci" # nixos-generate-config defaults
"usb_storage" # rpi needed this to boot from usb storage, i think.
"nvme" # to boot from nvme devices
# efi_pstore evivars
# boot.initrd.availableKernelModules = [
# "xhci_pci" "ahci" "sd_mod" "sdhci_pci" # nixos-generate-config defaults
# "usb_storage" # rpi needed this to boot from usb storage, i think.
# "nvme" # to boot from nvme devices
# # efi_pstore evivars
# added (speculatively) 2024/05/21; these were implicitly being added by nixos/modules/system/boot/kernel.nix
# i've copied not all of them, but most
"mmc_block"
"dm_mod"
# USB keyboards
"uhci_hcd" "ehci_hcd" "ehci_pci" "ohci_hcd" "ohci_pci" "xhci_hcd" "xhci_pci" "usbhid" "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat" "hid_logitech_hidpp" "hid_logitech_dj" "hid_microsoft" "hid_cherry" "hid_corsair"
# x86 keyboard stuff
"pcips2" "atkbd" "i8042"
# stage-2 init needs rtc?
"rtc_cmos"
];
# # added (speculatively) 2024/05/21; these were implicitly being added by nixos/modules/system/boot/kernel.nix
# # i've copied not all of them, but most
# "mmc_block"
# "dm_mod"
# # USB keyboards
# "uhci_hcd" "ehci_hcd" "ehci_pci" "ohci_hcd" "ohci_pci" "xhci_hcd" "xhci_pci" "usbhid" "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat" "hid_logitech_hidpp" "hid_logitech_dj" "hid_microsoft" "hid_cherry" "hid_corsair"
# # x86 keyboard stuff
# "pcips2" "atkbd" "i8042"
# # stage-2 init needs rtc?
# "rtc_cmos"
# ];
hardware.cpu.amd.updateMicrocode = true; # desko
hardware.cpu.intel.updateMicrocode = true; # lappy
hardware.cpu.intel.updateMicrocode = true; # flowy, lappy
boot.extraModprobeConfig = ''
# allow nested virtualization. XXX(2025-06-24): required for my work (?)
@@ -37,7 +37,5 @@ in
options kvm_amd nested=1
options kvm_intel nested=1
'';
sane.image.extraBootFiles = [ pkgs.bootpart-systemd-boot ];
};
}

View File

@@ -8,6 +8,7 @@ let
pname = "ip";
pkgs = [ "iproute2" "systemdMinimal" ];
srcRoot = ./.;
doInstallCheck = false; #< doesn't implement required `--help` command
};
in symlinkJoin {
name = "tailscale-iproute2";

View File

@@ -104,134 +104,162 @@ let
};
in
{
config = lib.mkIf config.sane.roles.work {
sane.persist.sys.byStore.private = [
{ user = "root"; group = "root"; mode = "0700"; path = "/var/lib/tailscale"; method = "bind"; }
];
services.tailscale.enable = true;
services.tailscale.package = tailscale;
systemd.services.tailscaled.environment.TS_DEBUG_USE_IP_COMMAND = "1";
# "statically" configure the routes to tailscale.
# tailscale doesn't use the kernel wireguard module,
# but a userspace `wireguard-go` (coupled with `/dev/net/tun`, or a pure
# pasta-style TCP/UDP userspace dev).
#
# it therefore appears as an "unmanaged" device to network managers like systemd-networkd.
# in order to configure routes, we have to script it.
systemd.services.tailscaled.serviceConfig.ExecStartPost = [
(pkgs.writeShellScript "tailscaled-add-routes" ''
while ! ${lib.getExe' tailscale "tailscale"} status ; do
echo "tailscale not ready"
sleep 2
done
for addr in ${lib.concatStringsSep " " routableSubnets}; do
(set -x ; ${ip} route add table main "$addr" dev tailscale0 scope global)
done
'')
];
systemd.services.tailscaled.preStop = ''
for addr in ${lib.concatStringsSep " " routableSubnets}; do
(set -x ; ${ip} route del table main "$addr" dev tailscale0 scope global) || true
done
'';
# systemd.network.networks."50-tailscale" = {
# # see: `man 5 systemd.network`
# matchConfig.Name = "tailscale0";
# routes = [
# # {
# # Scope = "global";
# # # 0.0.0.0/8 is a reserved-for-local-network range in IPv4
# # Destination = "0.0.0.0/8";
# # }
# {
# Scope = "global";
# # Scope = "link";
# # 10.0.0.0/8 is a reserved-for-private-networks range in IPv4
# Destination = "10.0.0.0/8";
# }
# {
# Scope = "global";
# # Scope = "link";
# # 100.64.0.0/10 is a reserved range in IPv4
# Destination = "100.64.0.0/10";
# }
# ];
# # RequiredForOnline => should `systemd-networkd-wait-online` fail if this network can't come up?
# linkConfig.RequiredForOnline = false;
# linkConfig.Unmanaged = lib.mkForce false; #< tailscale nixos module declares this as unmanaged
# };
# services.tailscale.useRoutingFeatures = "client";
services.tailscale.extraSetFlags = [
# --accept-routes does _two_ things:
# 1. allows tailscale to discover, internally, how to route to peers-of-peers.
# 2. instructs tailscale to tell the kernel to route discovered routes through the tailscale0 device.
# even if i disable #2, i still need --accept-routes to provide #1.
"--accept-routes"
# "--operator=colin" #< this *should* allow non-root control, but fails: <https://github.com/tailscale/tailscale/issues/16080>
# lock the preferences i care about, because even if they're default i think they _might_ be conditional on admin policy:
# --accept-dns=false:
# 1. i manage DNS (/etc/resolv.conf) manually, with BIND/nixos
# 2. `tailscale dns query ...` works only if `--accept-dns` is set FALSE.
# maybe because `--accept-dns=true` causes tailscaled to fail to write resolvconf, and then it aborts, or something...
"--accept-dns=false"
# "--accept-routes=false"
"--advertise-connector=false"
"--advertise-exit-node=false"
# "--auto-update=false" # "automatic updates are not supported on this platform"
"--ssh=false"
"--update-check=false"
"--webclient=false"
];
services.tailscale.extraDaemonFlags = [
"-verbose" "7"
];
services.bind.extraConfig = ''
include "${config.sops.secrets."tailscale-work-zones-bind.conf".path}";
'';
systemd.services.tailscaled = {
# systemd hardening (systemd-analyze security tailscaled.service)
serviceConfig.AmbientCapabilities = "CAP_NET_ADMIN";
serviceConfig.CapabilityBoundingSet = "CAP_NET_ADMIN";
serviceConfig.LockPersonality = true;
serviceConfig.MemoryDenyWriteExecute = true;
serviceConfig.NoNewPrivileges = true;
serviceConfig.ProtectClock = true;
serviceConfig.ProtectControlGroups = true;
serviceConfig.ProtectHome = true;
serviceConfig.ProtectHostname = true;
serviceConfig.ProtectKernelLogs = true;
serviceConfig.ProtectKernelModules = true;
serviceConfig.ProtectKernelTunables = true;
serviceConfig.ProtectProc = "invisible";
serviceConfig.ProtectSystem = "strict"; # makes read-only: all but /dev, /proc, /sys.
serviceConfig.ProcSubset = "pid";
# serviceConfig.PrivateIPC = true;
serviceConfig.PrivateTmp = true;
# serviceConfig.RemoveIPC = true; #< does not apply to root
serviceConfig.RestrictAddressFamilies = "AF_INET AF_INET6 AF_NETLINK AF_UNIX";
# #VVV this includes anything it reads from, e.g. /bin/sh; /nix/store/...
# # see `systemd-analyze filesystems` for a full list
serviceConfig.RestrictFileSystems = "@application @basic-api @common-block";
serviceConfig.RestrictRealtime = true;
serviceConfig.RestrictSUIDSGID = true;
serviceConfig.SystemCallArchitectures = "native";
serviceConfig.SystemCallFilter = [
"@system-service"
"@sandbox"
"~@chown"
"~@cpu-emulation"
"~@keyring"
config = lib.mkMerge [
(lib.mkIf config.sane.roles.work {
sane.persist.sys.byStore.private = [
{ user = "root"; group = "root"; mode = "0700"; path = "/var/lib/tailscale"; method = "bind"; }
];
serviceConfig.DevicePolicy = "closed"; # only allow /dev/{null,zero,full,random,urandom}
serviceConfig.DeviceAllow = "/dev/net/tun";
serviceConfig.RestrictNamespaces = true;
};
};
services.tailscale.enable = true;
services.tailscale.package = tailscale;
systemd.services.tailscaled.environment.TS_DEBUG_USE_IP_COMMAND = "1";
# "statically" configure the routes to tailscale.
# tailscale doesn't use the kernel wireguard module,
# but a userspace `wireguard-go` (coupled with `/dev/net/tun`, or a pure
# pasta-style TCP/UDP userspace dev).
#
# it therefore appears as an "unmanaged" device to network managers like systemd-networkd.
# in order to configure routes, we have to script it.
systemd.services.tailscaled.serviceConfig.ExecStartPost = [
(pkgs.writeShellScript "tailscaled-add-routes" ''
while ! ${lib.getExe' tailscale "tailscale"} status ; do
echo "tailscale not ready"
sleep 2
done
for addr in ${lib.concatStringsSep " " routableSubnets}; do
(set -x ; ${ip} route add table main "$addr" dev tailscale0 scope global)
done
'')
];
systemd.services.tailscaled.preStop = ''
for addr in ${lib.concatStringsSep " " routableSubnets}; do
(set -x ; ${ip} route del table main "$addr" dev tailscale0 scope global) || true
done
'';
# systemd.network.networks."50-tailscale" = {
# # see: `man 5 systemd.network`
# matchConfig.Name = "tailscale0";
# routes = [
# # {
# # Scope = "global";
# # # 0.0.0.0/8 is a reserved-for-local-network range in IPv4
# # Destination = "0.0.0.0/8";
# # }
# {
# Scope = "global";
# # Scope = "link";
# # 10.0.0.0/8 is a reserved-for-private-networks range in IPv4
# Destination = "10.0.0.0/8";
# }
# {
# Scope = "global";
# # Scope = "link";
# # 100.64.0.0/10 is a reserved range in IPv4
# Destination = "100.64.0.0/10";
# }
# ];
# # RequiredForOnline => should `systemd-networkd-wait-online` fail if this network can't come up?
# linkConfig.RequiredForOnline = false;
# linkConfig.Unmanaged = lib.mkForce false; #< tailscale nixos module declares this as unmanaged
# };
# services.tailscale.useRoutingFeatures = "client";
services.tailscale.extraSetFlags = [
# --accept-routes does _two_ things:
# 1. allows tailscale to discover, internally, how to route to peers-of-peers.
# 2. instructs tailscale to tell the kernel to route discovered routes through the tailscale0 device.
# even if i disable #2, i still need --accept-routes to provide #1.
"--accept-routes"
# "--operator=colin" #< this *should* allow non-root control, but fails: <https://github.com/tailscale/tailscale/issues/16080>
# lock the preferences i care about, because even if they're default i think they _might_ be conditional on admin policy:
# --accept-dns=false:
# 1. i manage DNS (/etc/resolv.conf) manually, with BIND/nixos
# 2. `tailscale dns query ...` works only if `--accept-dns` is set FALSE.
# maybe because `--accept-dns=true` causes tailscaled to fail to write resolvconf, and then it aborts, or something...
"--accept-dns=false"
# "--accept-routes=false"
"--advertise-connector=false"
"--advertise-exit-node=false"
# "--auto-update=false" # "automatic updates are not supported on this platform"
"--ssh=false"
"--update-check=false"
"--webclient=false"
];
services.tailscale.extraDaemonFlags = [
"-verbose" "7"
];
systemd.services.tailscaled = {
# systemd hardening (systemd-analyze security tailscaled.service)
serviceConfig.AmbientCapabilities = "CAP_NET_ADMIN";
serviceConfig.CapabilityBoundingSet = "CAP_NET_ADMIN";
serviceConfig.LockPersonality = true;
serviceConfig.MemoryDenyWriteExecute = true;
serviceConfig.NoNewPrivileges = true;
serviceConfig.ProtectClock = true;
serviceConfig.ProtectControlGroups = true;
serviceConfig.ProtectHome = true;
serviceConfig.ProtectHostname = true;
serviceConfig.ProtectKernelLogs = true;
serviceConfig.ProtectKernelModules = true;
serviceConfig.ProtectKernelTunables = true;
serviceConfig.ProtectProc = "invisible";
serviceConfig.ProtectSystem = "strict"; # makes read-only: all but /dev, /proc, /sys.
serviceConfig.ProcSubset = "pid";
# serviceConfig.PrivateIPC = true;
serviceConfig.PrivateTmp = true;
# serviceConfig.RemoveIPC = true; #< does not apply to root
serviceConfig.RestrictAddressFamilies = "AF_INET AF_INET6 AF_NETLINK AF_UNIX";
# #VVV this includes anything it reads from, e.g. /bin/sh; /nix/store/...
# # see `systemd-analyze filesystems` for a full list
serviceConfig.RestrictFileSystems = "@application @basic-api @common-block";
serviceConfig.RestrictRealtime = true;
serviceConfig.RestrictSUIDSGID = true;
serviceConfig.SystemCallArchitectures = "native";
serviceConfig.SystemCallFilter = [
"@system-service"
"@sandbox"
"~@chown"
"~@cpu-emulation"
"~@keyring"
];
serviceConfig.DevicePolicy = "closed"; # only allow /dev/{null,zero,full,random,urandom}
serviceConfig.DeviceAllow = "/dev/net/tun";
serviceConfig.RestrictNamespaces = true;
};
})
(lib.mkIf config.services.bind.enable {
# make DNS resolvable, if using BIND
sops.secrets."tailscale-work-zones-bind.conf".owner = "named";
services.bind.extraConfig = ''
include "${config.sops.secrets."tailscale-work-zones-bind.conf".path}";
'';
})
(lib.mkIf config.services.kresd.enable {
# make DNS resolvable, if using kresd
sops.secrets."tailscale-work-zones-kresd.conf".owner = "knot-resolver";
systemd.services."kresd@".serviceConfig = let
package = config.services.kresd.package;
in {
ExecStart = lib.mkForce [
"" #< clear previous assignment
(
# override default CLI so as to inject `-c` for secret config portion
# TODO: refactor for cleaner integration with hosts/common/net/dns/kresd.nix
"${package}/bin/kresd --noninteractive"
+ " -c ${package}/lib/knot-resolver/distro-preconfig.lua"
+ " -c /etc/knot-resolver/kresd.conf"
+ " -c ${config.sops.secrets."tailscale-work-zones-kresd.conf".path}"
)
];
};
})
];
}

View File

@@ -6,35 +6,80 @@
# for setup, see: <https://www.rsync.net/resources/howto/ssh_keys.html>
# - requires my pubkey to be copied to .ssh/authorized_keys on the remote.
set -xeu
set -eu
# secret should include RN_USER
source /run/secrets/rsync-net-env
RN_ID=/run/secrets/rsync-net-id_ed25519
PREFIX=$(hostname)
usage() {
echo 'sane-backup-rsync-net DIRECTORY...'
echo
echo "sync's each DIRECTORY with its counterpart stored on my rsync.net account"
echo
echo "on success, the remote directory will be an exact logical replica oc the local directory"
echo "that is, files present under the remote directory but NOT present under the local directory are deleted"
echo
echo "exits 1 if any directory fails to sync"
}
test -n "$PREFIX" && test -n "$RN_USER" && test -f "$RN_ID"
dirs=()
parseArgs() {
while [ "$#" -ne 0 ]; do
local arg=$1
shift
if (( ${#dirs[@]} )); then
# all remaining arguments are to be forwarded to the subcommand
dirs+=("$arg")
else
case $arg in
(--help)
usage
exit 0
;;
(*)
dirs=("$arg")
;;
esac
fi
done
rc=
for dir in "$@"; do
if [[ "$dir" != */ ]]; then
dir="$dir/"
if [ -z "$dirs" ]; then
usage
exit 1
fi
remote_dir="$RN_USER@$RN_USER.rsync.net:$PREFIX$dir"
}
now=$(date '+%s')
echo "syncing '$dir' to '$remote_dir'"
echo "$now" > "$dir"/zzz-rsync-net/last-attempted
# N.B.: manual flags instead of `-a -> -rlptgoD` because device files have a max path length which is too restricted
# TODO: add `sane-vpn do unmetered --`, after fixing pasta/sane-vpn to preserve capabilities + not create a new user namespace unconditionally.
# until then, don't run over cellular!
if rsync --exclude="$RN_ID" -e "ssh -i $RN_ID" --mkpath -rlptgov --delete "$dir" "$remote_dir"; then
echo "$now" > "$dir"/zzz-rsync-net/last-completed
rc=0$rc
else
rc=1
echo "FAILED TO BACKUP $dir"
fi
done
main() {
# secret should include RN_USER
source /run/secrets/rsync-net-env
local RN_ID=/run/secrets/rsync-net-id_ed25519
local PREFIX=$(hostname)
test -n "$rc" && exit $rc
test -n "$PREFIX" && test -n "$RN_USER" && test -f "$RN_ID"
local rc=
for dir in "$@"; do
if [[ "$dir" != */ ]]; then
dir="$dir/"
fi
local remote_dir="$RN_USER@$RN_USER.rsync.net:$PREFIX$dir"
local now=$(date '+%s')
echo "syncing '$dir' to '$remote_dir'"
echo "$now" > "$dir"/zzz-rsync-net/last-attempted
# N.B.: manual flags instead of `-a -> -rlptgoD` because device files have a max path length which is too restricted
# TODO: add `sane-vpn do unmetered --`, after fixing pasta/sane-vpn to preserve capabilities + not create a new user namespace unconditionally.
# until then, don't run over cellular!
if rsync --exclude="$RN_ID" -e "ssh -i $RN_ID" --mkpath -rlptgov --delete "$dir" "$remote_dir"; then
echo "$now" > "$dir"/zzz-rsync-net/last-completed
rc=0$rc
else
rc=1
echo "FAILED TO BACKUP $dir"
fi
done
test -n "$rc" && exit $rc
}
parseArgs "$@"
set -x
main "${dirs[@]}"

View File

@@ -0,0 +1,7 @@
{
"description": "The Final Straw Radio is a weekly, anarchist show eminating from occupied Cherokee lands in so-called North Carolina and featuring the voices of folks engaged in struggles for liberation and the creation of rad culture since 2009. We're also syndicated on a few community radio stations around the U.S. We frequently also feature radio commentaries from anarchist prisoner Sean Swain and are a proud member of CZN (The Channel Zero Network) and ARN (The A-Radio Network).\n\nCheck out our past archives and ways to connect with us at https://thefinalstrawradio.noblogs.org",
"is_podcast": true,
"title": "The Final Straw Radio",
"url": "https://thefinalstrawradio.libsyn.com/rss",
"velocity": 0.166
}

View File

@@ -280,8 +280,11 @@ in
bootFsImg
nixFsImg
];
firstPartGap = cfg.firstPartGap;
sectorSize = cfg.sectorSize;
inherit (cfg)
firstPartGap
sectorSize
extraGPTPadding
;
passthru = {
inherit bootFsImg nixFsImg;
};
@@ -307,8 +310,9 @@ in
mkdir -p $out
# 34 is the base GPT header size, as added to -p by cgpt.
# more precisely: PMBR (1 block) + Primary GPT header (1 block) + Primary GPT Table (32 blocks)
gptSize=$((34*512))
part0Start=$(( $gptSize + $firstPartGap ))
part0Start=$(( $extraGPTPadding + $gptSize + $firstPartGap ))
(
# solve for the size of the disk image
@@ -326,11 +330,11 @@ in
truncate -s $totalSize $out/disk.img
# Zeroes the GPT
cgpt create -z $out/disk.img
# Create the GPT with space if desired
cgpt create -p 0 $out/disk.img
( set -x ; cgpt create -z $out/disk.img )
# Create the GPT, optionally with some extra padding between the primary GPT header and the primary GPT table
( set -x ; cgpt create -p $(( $extraGPTPadding / $sectorSize )) $out/disk.img )
# Add the PMBR
cgpt boot -p $out/disk.img
( set -x ; cgpt boot -p $out/disk.img )
)
(
@@ -376,9 +380,10 @@ in
'' else ''
cp ${img}/disk.img $out
chmod +w $out
set -x
${cfg.installBootloader}
set +x
(
set -x
${cfg.installBootloader}
)
chmod -w $out
''
);
@@ -390,13 +395,15 @@ in
'')
]
++
lib.optionals config.boot.loader.systemd-boot.enable [
lib.optionals config.boot.loader.systemd-boot.enable ([
pkgs.bootpart-systemd-boot
# it'd be cool to use `config.system.build.installBootLoader` to install both the bootloader config AND the bootloader itself,
# but the combination of custom nixpkgs logic + systemd's sanity checking makes it near impossible to use
# outside a live system.
# so manually generate a bootloader entry:
(pkgs.runCommandLocal "populate-systemd-boot" {} ''
toplevel=${config.system.build.toplevel}
dtbpath=${if config.hardware.deviceTree.name != null then config.hardware.deviceTree.name else ""}
kernel_params=$(cat "$toplevel/kernel-params")
kernel=$(readlink "$toplevel/kernel")
@@ -411,14 +418,25 @@ in
efi_initrd="/EFI/nixos/$initrd_name.efi"
install -Dm644 "$initrd" "$out/$efi_initrd"
efi_dtb=
if [ -n "$dtbpath" ]; then
dtbs=$(readlink "$toplevel/dtbs")
dtbs_name="''${dtbs/\/nix\/store\//}"
dtbs_name="''${dtbs_name/\//-}"
# nixos-generated devicetree path is relative to /boot instead of being fully qualified, for some reason.
efi_dtb="EFI/nixos/$dtbs_name-$(basename $dtbpath).efi"
install -Dm644 "$dtbs/$dtbpath" "$out/$efi_dtb"
fi
mkdir -p $out/loader/entries
cat > $out/loader/entries/nixos-generation-0.conf <<EOF
title NixOS
sort-key nixos
version Generation 0 NixOS
linux $efi_kernel
initrd $initrd
initrd $efi_initrd
options init=$toplevel/init $kernel_params
''${efi_dtb:+devicetree $efi_dtb}
EOF
cat > $out/loader/loader.conf <<EOF
timeout 5
@@ -426,7 +444,7 @@ in
console-mode keep
EOF
'')
]
])
;
};
}

View File

@@ -1,15 +1,44 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p bash -p coreutils-full -p gocryptfs -p inotify-tools
passfile="$1" # e.g. /run/gocryptfs/private.key
conffile="$2" # e.g. /nix/persist/private/gocryptfs.conf
passdir=$(dirname "$passfile")
usage() {
echo "provision-private-key PASSFILE CONFFILE"
echo "e.g. provision-private-key /run/gocryptfs/private.key /nix/persist/private/gocryptfs.conf"
}
# passfile="$1" # e.g. /run/gocryptfs/private.key
# conffile="$2" # e.g. /nix/persist/private/gocryptfs.conf
passfile=
conffile=
parseArgs() {
while [ "$#" -ne 0 ]; do
local arg=$1
shift
case $arg in
(--help)
usage
exit 0
;;
(*)
if [ -z "$passfile" ]; then
passfile=$arg
elif [ -z "$conffile" ]; then
conffile=$arg
else
usage
exit 1
fi
;;
esac
done
}
waitForPassfileOnce() {
local timeout=$1
if [ -f "$passfile" ]; then
return 0
else
local passdir=$(dirname "$passfile")
# wait for some file to be created inside the directory.
# inotifywait returns 0 if the file was created. 1 or 2 if timeout was hit or it was interrupted by a different event.
inotifywait --timeout "$timeout" --event create "$passdir"
@@ -40,8 +69,14 @@ validatePassword() {
fi
}
waitForPassfile
while ! validatePassword; do
main() {
waitForPassfile
done
echo "key provisioned"
while ! validatePassword; do
waitForPassfile
done
echo "key provisioned"
}
parseArgs "$@"
main

View File

@@ -17,13 +17,13 @@ in
enable = mkEnableOption "keep track of the public WAN address of this machine, as viewed externally";
ipPath = mkOption {
default = "/var/lib/uninsane/wan.txt";
default = "/var/lib/dyn-dns/wan.txt";
type = types.str;
description = "where to store the latest WAN IPv4 address";
};
upnpPath = mkOption {
default = "/var/lib/uninsane/upnp.txt";
default = "/var/lib/dyn-dns/upnp.txt";
type = types.str;
description = ''
where to store the address of the UPNP device (if any) that can be used to create port forwards.
@@ -56,6 +56,9 @@ in
};
config = mkIf cfg.enable {
sane.persist.sys.byStore.plaintext = [
{ user = "root"; group = "root"; mode = "0755"; path = "/var/lib/dyn-dns"; method = "bind"; }
];
systemd.services.dyn-dns = {
description = "update this host's record of its WAN IP";
serviceConfig.Type = "oneshot";

View File

@@ -45,7 +45,7 @@ let
'';
example = {
"%CNAMESELF%" = "lappy";
"%AWAN%" = ''"$(cat /var/uninsane/wan.txt)"'';
"%AWAN%" = ''"$(cat /var/lib/dyn-dns/wan.txt)"'';
};
};
includes = mkOption {
@@ -163,8 +163,8 @@ let
[ "${config.services.hickory-dns.configFile}" ]
[ configPath ]
config.systemd.services.hickory-dns.serviceConfig.ExecStart;
# servo/dyn-dns needs /var/lib/uninsane/wan.txt.
ReadOnlyPaths = lib.optionals config.sane.services.dyn-dns.enable [ "/var/lib/uninsane" ];
# servo/dyn-dns needs /var/lib/dyn-dns/wan.txt.
ReadOnlyPaths = lib.optionals config.sane.services.dyn-dns.enable [ "/var/lib/dyn-dns" ];
} // lib.optionalAttrs cfg.asSystemResolver {
# allow the group to write hickory-dns state (needed by NetworkManager hook)
StateDirectoryMode = "775";

View File

@@ -1,39 +1,6 @@
# outstanding cross-compilation PRs/issues:
# - all: <https://github.com/NixOS/nixpkgs/labels/6.topic%3A%20cross-compilation>
# - qtsvg mixed deps: <https://github.com/NixOS/nixpkgs/issues/269756>
# - big Qt fix: <https://github.com/NixOS/nixpkgs/pull/267311>
#
# outstanding issues:
# - 2023/10/10: build python3 is pulled in by many things
# - nix why-depends --all /nix/store/8g3kd2jxifq10726p6317kh8srkdalf5-nixos-system-moby-23.11.20231011.dirty /nix/store/pzf6dnxg8gf04xazzjdwarm7s03cbrgz-python3-3.10.12/bin/python3.10
# - gstreamer-vaapi -> gstreamer-dev -> glib-dev
# - portfolio -> {glib,cairo,pygobject}-dev
# - komikku -> python3.10-brotlicffi -> python3.10-cffi
# - many others. python3.10-cffi seems to be the offender which infects 70% of consumers though
# - 2023/10/11: build ruby is pulled in by `neovim`:
# - nix why-depends --all /nix/store/rhli8vhscv93ikb43639c2ysy3a6dmzp-nixos-system-moby-23.11.20231011.30c7fd8 /nix/store/5xbwwbyjmc1xvjzhghk6r89rn4ylidv8-ruby-3.1.4
# - 2023/12/19: rustPlatform.cargoSetupHook outside of `buildRustPackage` or python packages is a mess
# - it doesn't populate `.cargo/config` with valid cross-compilation config
# - something to do with the way it's spliced: `nativeBuildInputs = [ rustPlatform.cargoSetupHook.__spliced.hostHost ]` (or hostTarget) WORKS
# - see <https://github.com/NixOS/nixpkgs/pull/260068> -- it's probably wrong.
# - WIP fix in `pr-cross-cargo`/`pr-cross-cargo2` nixpkgs branch.
# - sanity check by building `pkgsCross.aarch64-multiplatform.rav1e`, and the `fd` program mentioned in PR 260068
# - `pkgsCross.musl64.fd`
# - `pkgsStatic.fd`
# - this is way too tricky to enable cross compilation without breaking the musl stuff.
# - i lost a whole day trying to get it to work: don't do it!
#
# partially fixed:
# - 2023/10/11: build coreutils pulled in by rpm 4.18.1, but NOT by 4.19.0
# - nix why-depends --all /nix/store/gjwd2x507x7gjycl5q0nydd39d3nkwc5-dtrx-8.5.3-aarch64-unknown-linux-gnu /nix/store/y9gr7abwxvzcpg5g73vhnx1fpssr5frr-coreutils-9.3
#
# outstanding issues for software i don't have deployed:
# - gdk-pixbuf doesn't generate `gdk-pixbuf-thumbnailer` on cross
# - been this way since 2018: <https://gitlab.gnome.org/GNOME/gdk-pixbuf/-/merge_requests/20>
# - as authored upstream, thumbnailer depends on loader.cache, and neither are built during cross compilation.
# - nixos manually builds loader.cache in postInstall (via emulator).
# - even though we have loader.cache, ordering means that thumbnailer still can't be built.
# - solution is probably to integrate meson's cross_file stuff, and pushing all this emulation upstream.
# tracking:
# - all cross compilation PRs: <https://github.com/NixOS/nixpkgs/labels/6.topic%3A%20cross-compilation>
# - potential idiom to fix cross cargo-inside-meson: <https://github.com/NixOS/nixpkgs/pull/434878>
final: prev:
let
@@ -85,63 +52,63 @@ let
typelibPath = pkgs: lib.concatStringsSep ":" (builtins.map (p: "${lib.getLib p}/lib/girepository-1.0") pkgs);
# `cargo` which adds the correct env vars and `--target` flag when invoked from meson build scripts
crossCargo = let
inherit (final.pkgsBuildHost) cargo;
inherit (final.rust.envVars) setEnv rustHostPlatformSpec;
in (final.pkgsBuildBuild.writeShellScriptBin "cargo" ''
targetDir=target
isFlavored=
outDir=
profile=
# crossCargo = let
# inherit (final.pkgsBuildHost) cargo;
# inherit (final.rust.envVars) setEnv rustHostPlatformSpec;
# in (final.pkgsBuildBuild.writeShellScriptBin "cargo" ''
# targetDir=target
# isFlavored=
# outDir=
# profile=
cargoArgs=("$@")
nextIsOutDir=
nextIsProfile=
nextIsTargetDir=
for arg in "''${cargoArgs[@]}"; do
if [[ -n "$nextIsOutDir" ]]; then
nextIsOutDir=
outDir="$arg"
elif [[ -n "$nextIsProfile" ]]; then
nextIsProfile=
profile="$arg"
elif [[ -n "$nextIsTargetDir" ]]; then
nextIsTargetDir=
targetDir="$arg"
elif [[ "$arg" = "build" ]]; then
isFlavored=1
elif [[ "$arg" = "--out-dir" ]]; then
nextIsOutDir=1
elif [[ "$arg" = "--profile" ]]; then
nextIsProfile=1
elif [[ "$arg" = "--release" ]]; then
profile=release
elif [[ "$arg" = "--target-dir" ]]; then
nextIsTargetDir=1
fi
done
# cargoArgs=("$@")
# nextIsOutDir=
# nextIsProfile=
# nextIsTargetDir=
# for arg in "''${cargoArgs[@]}"; do
# if [[ -n "$nextIsOutDir" ]]; then
# nextIsOutDir=
# outDir="$arg"
# elif [[ -n "$nextIsProfile" ]]; then
# nextIsProfile=
# profile="$arg"
# elif [[ -n "$nextIsTargetDir" ]]; then
# nextIsTargetDir=
# targetDir="$arg"
# elif [[ "$arg" = "build" ]]; then
# isFlavored=1
# elif [[ "$arg" = "--out-dir" ]]; then
# nextIsOutDir=1
# elif [[ "$arg" = "--profile" ]]; then
# nextIsProfile=1
# elif [[ "$arg" = "--release" ]]; then
# profile=release
# elif [[ "$arg" = "--target-dir" ]]; then
# nextIsTargetDir=1
# fi
# done
extraFlags=()
# extraFlags=()
# not all subcommands support flavored arguments like `--target`
if [ -n "$isFlavored" ]; then
# pass the target triple to cargo so it will cross compile
# and fix so it places outputs in the same directory as non-cross, see: <https://doc.rust-lang.org/cargo/guide/build-cache.html>
extraFlags+=(
--target "${rustHostPlatformSpec}"
-Z unstable-options
)
if [ -z "$outDir" ]; then
extraFlags+=(
--out-dir "$targetDir"/''${profile:-debug}
)
fi
fi
# # not all subcommands support flavored arguments like `--target`
# if [ -n "$isFlavored" ]; then
# # pass the target triple to cargo so it will cross compile
# # and fix so it places outputs in the same directory as non-cross, see: <https://doc.rust-lang.org/cargo/guide/build-cache.html>
# extraFlags+=(
# --target "${rustHostPlatformSpec}"
# -Z unstable-options
# )
# if [ -z "$outDir" ]; then
# extraFlags+=(
# --out-dir "$targetDir"/''${profile:-debug}
# )
# fi
# fi
exec ${setEnv} "${lib.getExe cargo}" "$@" "''${extraFlags[@]}"
'').overrideAttrs {
inherit (cargo) meta;
};
# exec ${setEnv} "${lib.getExe cargo}" "$@" "''${extraFlags[@]}"
# '').overrideAttrs {
# inherit (cargo) meta;
# };
in with final; {
# binutils = prev.binutils.override {
# # fix that resulting binary files would specify build #!sh as their interpreter.
@@ -153,7 +120,7 @@ in with final; {
# };
# 2025/07/27: upstreaming is unblocked, but a cleaner solution than this doesn't seem to exist yet
# 2025/08/31: upstreaming is unblocked, but a cleaner solution than this doesn't seem to exist yet
confy = prev.confy.overrideAttrs (upstream: {
# meson's `python.find_installation` method somehow just doesn't support cross compilation.
# - <https://mesonbuild.com/Python-module.html#find_installation>
@@ -171,11 +138,6 @@ in with final; {
'';
});
# 2025/07/27: upstreaming is unblocked
delfin = prev.delfin.override {
cargo = crossCargo;
};
# 2025/07/27: upstreaming is unblocked
# dtrx = prev.dtrx.override {
# # `binutils` is the nix wrapper, which reads nix-related env vars
@@ -185,18 +147,18 @@ in with final; {
# binutils = binutils-unwrapped;
# };
# 2025/07/27: upstreaming is unblocked
# envelope = prev.envelope.override {
# cargo = crossCargo;
# };
# 2025/08/31: upstreaming is blocked on mailutils -> gss -> shishi
# emacs = prev.emacs.override {
# nativeComp = false; # will be renamed to `withNativeCompilation` in future
# # future: we can specify 'action-if-cross-compiling' to actually invoke the test programs:
# # <https://www.gnu.org/software/autoconf/manual/autoconf-2.63/html_node/Runtime.html>
# };
envelope = prev.envelope.override {
cargo = crossCargo; #< fixes openssl not being able to find its library
};
# 2025/07/27: upstreaming is unblocked
# 2025/08/31: upstreaming is unblocked
# firejail = prev.firejail.overrideAttrs (upstream: {
# # firejail executes its build outputs to produce the default filter list.
# # i think we *could* copy the default filters from pkgsBuildBuild, but that doesn't seem future proof
@@ -210,7 +172,7 @@ in with final; {
# '');
# });
# 2025/07/27: upstreaming is unblocked
# 2025/08/31: upstreaming is unblocked
# flare-signal = prev.flare-signal.overrideAttrs (upstream: {
# env = let
# inherit buildPackages stdenv rust;
@@ -232,21 +194,16 @@ in with final; {
# };
# });
# 2025/07/27: upstreaming is blocked by glycin-loaders
fractal = prev.fractal.override {
cargo = crossCargo;
};
# 2025/07/27: upstreaming is unblocked
glycin-loaders = prev.glycin-loaders.override {
cargo = crossCargo;
};
# 2025/08/26: upstreaming is unblocked, out for PR: <https://github.com/NixOS/nixpkgs/pull/437038>
# fractal = prev.fractal.override {
# cargo = crossCargo;
# };
# 2025/07/27: upstreaming is blocked on gnome-shell
# fixes: "gdbus-codegen not found or executable"
# gnome-session = mvToNativeInputs [ glib ] super.gnome-session;
# 2025/07/27: upstreaming is blocked on ibus, evolution-data-server -> gnome-online-accounts -> gvfs -> ...
# 2025/08/31: upstreaming is blocked on evolution-data-server -> gnome-online-accounts -> gvfs -> ... -> ruby
# gnome-shell = super.gnome-shell.overrideAttrs (orig: {
# # fixes "meson.build:128:0: ERROR: Program 'gjs' not found or not executable"
# # does not fix "_giscanner.cpython-310-x86_64-linux-gnu.so: cannot open shared object file: No such file or directory" (python import failure)
@@ -271,11 +228,6 @@ in with final; {
# ];
# });
# 2025/07/27: upstreaming is unblocked
gnome-user-share = prev.gnome-user-share.override {
cargo = crossCargo;
};
# 2025/07/27: upstreaming is unblocked
# # gnustep is going to need a *lot* of work/domain-specific knowledge to truly cross-compile,
# gnustep-base = prev.gnustep-base.overrideAttrs (upstream: {
@@ -331,14 +283,10 @@ in with final; {
# nativeBuildInputs = lib.remove [ qt6.wrapQtAppsHook ] upstream.nativeBuildInputs;
# });
# 2025/07/27: upstreaming is unblocked -- but is this necessary?
# koreader = prev.koreader.overrideAttrs (upstream: {
# nativeBuildInputs = upstream.nativeBuildInputs ++ [
# autoPatchelfHook
# ];
# });
# 2025/09/06: upstreaming is blocked on xdp-tools; out for PR: <https://github.com/NixOS/nixpkgs/pull/442827>
# knot-dns = addNativeInputs [ buildPackages.protobufc ] prev.knot-dns;
lemoa = prev.lemoa.override { cargo = crossCargo; };
# lemoa = prev.lemoa.override { cargo = crossCargo; };
# libsForQt5 = prev.libsForQt5.overrideScope (self: super: {
# # 2025/07/27: upstreaming is blocked on qtsvg
@@ -354,11 +302,6 @@ in with final; {
# callPackage = self.newScope { inherit (self) qtCompatVersion qtModule srcs; inherit stdenv; };
# });
# 2025/07/27: upstreaming blocked on glycin-loaders
loupe = prev.loupe.override {
cargo = crossCargo;
};
# 2024/11/19: upstreaming is unblocked
mepo = (prev.mepo.override {
# nixpkgs mepo correctly puts `zig_0_13.hook` in nativeBuildInputs,
@@ -397,46 +340,6 @@ in with final; {
# 2025/07/27: upstreaming is unblocked by deps; but turns out to not be this simple
# ncftp = addNativeInputs [ bintools ] prev.ncftp;
# 2025/07/27: upstreaming is unblocked
newsflash = (prev.newsflash.override {
cargo = crossCargo;
}).overrideAttrs (upstream: {
postPatch = (upstream.postPatch or "") + ''
rm build.rs
export OUT_DIR=$(pwd)
# from build.rs:
glib-compile-resources --sourcedir=data/resources --target=icons.gresource data/resources/icons.gresource.xml
glib-compile-resources --sourcedir=data/resources --target=styles.gresource data/resources/styles.gresource.xml
substitute data/io.gitlab.news_flash.NewsFlash.appdata.xml.in.in \
data/resources/io.gitlab.news_flash.NewsFlash.appdata.xml \
--replace-fail '@appid@' 'io.gitlab.news_flash.NewsFlash'
glib-compile-resources --sourcedir=data/resources --target=appdata.gresource data/resources/appdata.gresource.xml
'';
env = let
ccForBuild = "${buildPackages.stdenv.cc}/bin/${buildPackages.stdenv.cc.targetPrefix}cc";
cxxForBuild = "${buildPackages.stdenv.cc}/bin/${buildPackages.stdenv.cc.targetPrefix}c++";
ccForHost = "${stdenv.cc}/bin/${stdenv.cc.targetPrefix}cc";
cxxForHost = "${stdenv.cc}/bin/${stdenv.cc.targetPrefix}c++";
rustBuildPlatform = stdenv.buildPlatform.rust.rustcTarget;
rustTargetPlatform = stdenv.hostPlatform.rust.rustcTarget;
in (upstream.env or {}) // {
# taken from <pkgs/build-support/rust/hooks/default.nix>
# fixes "cargo:warning=aarch64-unknown-linux-gnu-gcc: error: unrecognized command-line option -m64"
# XXX: these aren't necessarily valid environment variables: the referenced nix file is more clever to get them to work.
"CC_${rustBuildPlatform}" = "${ccForBuild}";
"CXX_${rustBuildPlatform}" = "${cxxForBuild}";
"CC_${rustTargetPlatform}" = "${ccForHost}";
"CXX_${rustTargetPlatform}" = "${cxxForHost}";
# fails to fix "Failed to find OpenSSL development headers."
# OPENSSL_NO_VENDOR = 1;
# OPENSSL_LIB_DIR = "${lib.getLib openssl}/lib";
# OPENSSL_DIR = "${lib.getDev openssl}";
};
});
# fixes "properties/gresource.xml: Permission denied"
# - by providing glib-compile-resources
# 2025/07/27: upstreaming is blocked on gst-plugins-good, qtkeychain, qtmultimedia, qtquick3d, qt-jdenticon
@@ -506,7 +409,7 @@ in with final; {
# # buildInputs = lib.remove gnupg upstream.buildInputs;
# });
# 2025/07/27: upstreaming is unblocked, but most of this belongs in _oils_ repo
# 2025/08/31: upstreaming is unblocked, but most of this belongs in _oils_ repo
oils-for-unix = prev.oils-for-unix.overrideAttrs (upstream: {
postPatch = (upstream.postPatch or "") + ''
substituteInPlace _build/oils.sh \
@@ -530,10 +433,10 @@ in with final; {
];
});
# 2025/07/27: upstreaming is blocked on gnome-user-share, nautilus
papers = prev.papers.override {
cargo = crossCargo;
};
# 2025/08/31: upstreaming is unblocked; out for review: <https://github.com/NixOS/nixpkgs/pull/437704>
# papers = prev.papers.override {
# cargo = crossCargo;
# };
# 2025/07/27: upstreaming is blocked on gnome-session (itself blocked on gnome-shell)
# phosh = prev.phosh.overrideAttrs (upstream: {
@@ -561,11 +464,6 @@ in with final; {
# ];
# } prev.phosh-mobile-settings;
# 2025/07/27: upstreaming is unblocked
pwvucontrol = prev.pwvucontrol.override {
cargo = crossCargo;
};
# pythonPackagesExtensions = prev.pythonPackagesExtensions ++ [
# (pyself: pysuper: {
# # 2025/07/23: upstreaming is unblocked, but solution is untested.
@@ -728,16 +626,16 @@ in with final; {
# # '';
# });
# 2025/07/27: upstreaming is blocked on glycin-loaders
snapshot = prev.snapshot.override {
# fixes "error: linker `cc` not found"
cargo = crossCargo;
};
# 2025/08/26: upstreaming is unblocked; implemented on desko `pr-snapshot-cross` branch
# snapshot = prev.snapshot.override {
# # fixes "error: linker `cc` not found"
# cargo = crossCargo;
# };
# 2025/07/27: upstreaming is unblocked
spot = prev.spot.override {
cargo = crossCargo;
};
# 2025/08/26: upstreaming is unblocked; patched on desko branch `pr-spot-cross`
# spot = prev.spot.override {
# cargo = crossCargo;
# };
# 2025/07/27: upstreaming is unblocked
# squeekboard = prev.squeekboard.overrideAttrs (upstream: {
@@ -783,7 +681,7 @@ in with final; {
# ];
# });
# 2025/07/27: upstreaming blocked on gvfs -> udisks -> libblockdev -> {thin-provisioning-tools,libndctl -> ... -> ruby}
# 2025/08/31: upstreaming blocked on gvfs -> udisks -> libblockdev -> {thin-provisioning-tools,libndctl -> ... -> ruby}
swaynotificationcenter = mvToNativeInputs [ buildPackages.wayland-scanner ] prev.swaynotificationcenter;
# 2025/07/27: upstreaming is unblocked
@@ -834,10 +732,10 @@ in with final; {
# });
# };
# 2025/05/01: upstreaming is unblocked
video-trimmer = prev.video-trimmer.override {
cargo = crossCargo;
};
# 2025/08/26: upstreaming is unblocked; implemented on desko branch `pr-video-trimmer-cross`
# video-trimmer = prev.video-trimmer.override {
# cargo = crossCargo;
# };
# 2025/01/13: upstreaming is blocked on arrow-cpp, python-pyarrow, python-contourpy, python-matplotlib, python-h5py, python-pandas, google-cloud-cpp
# visidata = prev.visidata.override {
@@ -868,4 +766,11 @@ in with final; {
# [ "false && $out/bin/nvim-wrapper" ]
# upstream.postBuild;
# });
# 2025/09/06: upstreaming is unblocked; out for PR: <https://github.com/NixOS/nixpkgs/pull/442827>
# xdp-tools = prev.xdp-tools.overrideAttrs {
# # when cross compiling, `clang` packages ships binary as `aarch64-...-clang` (wrapper),
# # and xdp-tools `configure` detects the unwrapped `clang` instead, doesn't receive nix flags
# CLANG = lib.getExe buildPackages.llvmPackages.clang;
# };
}

View File

@@ -23,7 +23,7 @@ in
# DISABLE HDCP BLOB in pinephone pro.
# this is used by u-boot; requires redeploying the bootloader (the SPL, specifically).
# i can see that nixpkgs does process this option, but the hash of bl31.elf doesn't actually change
arm-trusted-firmware = super.arm-trusted-firmware.override {
buildArmTrustedFirmware = super.buildArmTrustedFirmware.override {
unfreeIncludeHDCPBlob = false;
};

View File

@@ -0,0 +1,14 @@
# sourced, with modifications, from <https://github.com/pftf/RPi4/blob/master/config.txt>
arm_64bit=1
# arm_boost=1
# N.B.: `enable_uart=1` is required for systemd-boot's `timeout` (i.e. auto-boot default entry) feature
enable_uart=1
# uart_2ndstage=1
enable_gic=1
armstub=RPI_EFI.fd
# disable_commandline_tags=1
# disable_overscan=1
# device_tree_address=0x3e0000
# device_tree_end=0x400000
# dtoverlay=miniuart-bt
# dtoverlay=upstream-pi4

View File

@@ -0,0 +1,30 @@
{
edk2-rpi4,
raspberrypifw,
runCommandLocal,
}:
runCommandLocal "bootpart-edk2-rpi" {
meta = {
description = ''
unmanaged files to place in /boot on a Raspberry Pi 400 system.
these files are not enough on their own to boot a kernel,
but only to boot an EFI application.
best paired with systemd-boot (via `bootpart-systemd-boot`), or perhaps u-boot (untested).
'';
platforms = [
"aarch64-linux"
];
};
} ''
install -Dm644 ${edk2-rpi4}/RPI_EFI.fd $out/RPI_EFI.fd
install -Dm644 ${./config.txt} $out/config.txt
install -Dm644 ${raspberrypifw}/share/raspberrypi/boot/fixup4.dat $out/fixup4.dat
install -Dm644 ${raspberrypifw}/share/raspberrypi/boot/start4.elf $out/start4.elf
# N.B.: there are weird incompatibilities between raspberrypifw (start4.elf) and edk2.
# it seems that if the two binaries are on different versions, then the dtb resolves some discrepencies:
# - allows systemd-boot to autoboot after 5s (else, it lacks its countdown ... uart problems?)
# but generally, omitting this .dtb probably won't totally break the boot.
install -Dm644 ${raspberrypifw}/share/raspberrypi/boot/bcm2711-rpi-400.dtb $out/bcm2711-rpi-400.dtb
# install -Dm644 ${raspberrypifw}/share/raspberrypi/boot/bcm2711-rpi-4-b.dtb $out/bcm2711-rpi-4-b.dtb
# install -Dm644 ${raspberrypifw}/share/raspberrypi/boot/bcm2711-rpi-cm4.dtb $out/bcm2711-rpi-cm4.dtb
''

View File

@@ -0,0 +1,133 @@
# WHEN UPDATING:
# - check the tag in <repo:nixos/nixpkgs:pkgs/by-name/ed/edk2/package.nix>
# e.g. edk2-stable202505
# - figure the date, via `cd edk2 && git show edk2-stable202505`
# - pin the other repos to the closest commit
# consider updating to the known-good commits from <https://github.com/pftf/RPi4>
{
acpica-tools,
buildArmTrustedFirmware,
edk2,
fetchFromGitHub,
lib,
}:
let
armTrustedFirmwareRpi4 = buildArmTrustedFirmware rec {
# see: <repo:tianocore/edk2-non-osi:Platform/RaspberryPi/RPi4/TrustedFirmware/Readme.md>
# says they build it with:
# > make PLAT=rpi4 RPI3_PRELOADED_DTB_BASE=0x1F0000 PRELOADED_BL33_BASE=0x20000 SUPPORT_VFP=1 SMC_PCI_SUPPORT=1 DEBUG=0 all
# see also <repo:ARM-software/arm-trusted-firmware:plat/rpi/rpi4/platform.mk>
platform = "rpi4";
extraMeta.platforms = [ "aarch64-linux" ];
filesToInstall = [ "build/${platform}/release/bl31.bin" ];
# platformCanUseHDCPBlob = true;
extraMakeFlags = [
# these are the flags which tianocore, pftf build with.
# RPI3_PRELOADED_DTB_BASE: "Auxiliary build option needed when using `RPI3_DIRECT_LINUX_BOOT=1`. This option allows to specify the location of a DTB in memory."
# "RPI3_PRELOADED_DTB_BASE=0x1F0000"
# PRELOADED_BL33_BASE: "Used to specify the fixed address of a BL33 binary that has been preloaded by earlier boot stages (VPU). Useful for bundling BL31 and BL33 in the same `armstub` image (e.g. TF-A + EDK2)."
# IOW: ATF and EDK2 need to agree on the value of PRELOADED_BL33_BASE!
"PRELOADED_BL33_BASE=0x20000"
# SUPPORT_VFP: "allow Vector Floating Point operations in EL3".
# where EL3 is the part of TFA which stays resident in the CPU;
# ATF lowers to EL2 before passing control to the next boot stage;
# i'd guess this is an (optional) optimization of some sort?
# "SUPPORT_VFP=1"
# SMC_PCI_SUPPORT: ATF rpi4/platform.mk defaults this to 0 and says "SMCCC PCI support (should be enabled for ACPI builds)".
# "SMC_PCI_SUPPORT=1"
];
};
edk2-platforms = fetchFromGitHub {
owner = "tianocore";
repo = "edk2-platforms";
name = "edk2-platforms";
rev = "6153891ee3b4e80e0340236f0eec0228746bacc0"; # 2025-05-25
hash = "sha256-EuM6MXc39IJB8QAkW/DFagz2wLI1EYftvayP45oHz0I=";
};
# edk2-src = fetchFromGitHub {
# owner = "tianocore";
# repo = "edk2";
# name = "edk2";
# # fetchSubmodules = true;
# rev = "060bb0e5a75946729defa4824fa899cf4cc0528b";
# # hash = "sha256-gafZ+iyJ0IpGpe3ucPw/Ap/3ZrY3gCNSJEpAqgBAzRY=";
# };
# edk2-non-osi = fetchFromGitHub {
# owner = "tianocore";
# repo = "edk2-non-osi";
# name = "edk2-non-osi";
# rev = "0544808c623bb73252310b1e5ef887caaf08c34b"; # 2024-03-14
# hash = "sha256-09D1p7xHT6rLxgdw7flT3gEWNKqxOhM2Q643t0nw9ww=";
# # rev = "3415f616e08a0d9c7bd264cab674929a7b0f5e33"; # 2025-08-04
# };
in edk2.mkDerivation "Platform/RaspberryPi/RPi4/RPi4.dsc" {
pname = "edk2-rpi4";
inherit (edk2) version;
nativeBuildInputs = [
acpica-tools
];
srcs = [
edk2.src
edk2-platforms
];
sourceRoot = edk2.src.name;
postPatch = ''
# patch out the raspberry pi logo from the boot process, so as to remove dependency
# on separate edk2-non-osi repo (which i would otherwise have to keep in sync)...
# to instead build WITH the raspberry logo, remove these patches and set
# PACKAGES_PATH=$edk2:$edk2-platforms:$edk2-non-osi
#
# N.B.: can either comment out the `LogoDxe.inf` lines, to build with no logo,
# or point them to a different in-tree Logo (e.g. AMD, Intel, or tianocore (i.e. MdeModulePkg)).
substituteInPlace ../edk2-platforms/Platform/RaspberryPi/RPi4/RPi4.fdf \
--replace-fail \
'INF Platform/RaspberryPi/Drivers/LogoDxe/LogoDxe.inf' \
'INF MdeModulePkg/Logo/LogoDxe.inf'
substituteInPlace ../edk2-platforms/Platform/RaspberryPi/RPi4/RPi4.dsc \
--replace-fail \
'Platform/RaspberryPi/Drivers/LogoDxe/LogoDxe.inf' \
'MdeModulePkg/Logo/LogoDxe.inf'
export PACKAGES_PATH=$(pwd):$(pwd)/../edk2-platforms
'';
installPhase = ''
runHook preInstall
install -Dm644 Build/RPi4/RELEASE*/FV/RPI_EFI.fd $out/RPI_EFI.fd
runHook postInstall
'';
buildFlags = [
"-DTFA_BUILD_ARTIFACTS=${armTrustedFirmwareRpi4}" #< or, place bl31.bin at Platform/RaspberryPi/RPi4/TrustedFirmware
# flags taken from <https://github.com/pftf/RPi4/blob/master/.github/workflows/linux_edk2.yml#L63>
# in practice, none seem to be required
# "-DSECURE_BOOT_ENABLE=TRUE"
# "-DINCLUDE_TFTP_COMMAND=TRUE"
# "-DNETWORK_ISCSI_ENABLE=TRUE"
# "-DSMC_PCI_SUPPORT=1"
# "-DNETWORK_TLS_ENABLE=FALSE"
# "-DNETWORK_ALLOW_HTTP_CONNECTIONS=TRUE"
];
# fixes `error: -Wformat-security ignored without -Wformat`
env.NIX_CFLAGS_COMPILE = "-Wformat";
passthru = {
inherit
# edk2-non-osi
edk2-platforms
# edk2-src
armTrustedFirmwareRpi4
;
};
meta = {
maintainers = with lib.maintainers; [
colinsane
];
platforms = [ "aarch64-linux" ];
};
}

View File

@@ -9,9 +9,11 @@
libsoup_3,
meson,
ninja,
nix-update-script,
openssl,
pkg-config,
python3,
rust,
rustPlatform,
rustc,
stdenv,
@@ -19,20 +21,21 @@
wrapGAppsHook4,
}:
stdenv.mkDerivation {
stdenv.mkDerivation (finalAttrs: {
pname = "envelope";
version = "0.1.0-unstable-2024-09-13";
version = "0.1.0-unstable-2025-05-17";
src = fetchFromGitLab {
domain = "gitlab.gnome.org";
owner = "felinira";
repo = "envelope";
rev = "11ce86da13793787a25e48ca23322b33fcf8bf34"; # last commit before libadwaita 1.6
hash = "sha256-EX309RhisBx27TscMsibEvqCSCUSukTgf4Xs1Vws4YY=";
rev = "e2a8a56aa9b68d82486b99790b86322715d2a6db";
hash = "sha256-osVShCaKKoGhxWCjaYcMkOji8e0oETgDaDpCAfHauwQ=";
};
cargoDeps = rustPlatform.importCargoLock {
lockFile = ./Cargo.lock;
cargoDeps = rustPlatform.fetchCargoVendor {
inherit (finalAttrs) pname version src;
hash = "sha256-8pK8cw9nYJmmybYRL+PUCK8FvUUPbyFp7oYYF461KPc=";
};
nativeBuildInputs = [
@@ -58,10 +61,21 @@ stdenv.mkDerivation {
postPatch = ''
patchShebangs --build build-aux/meson-cargo-manifest.py
# versions prior to c3f5ed4f (2024-10-13) didn't embed Cargo.lock
cp ${./Cargo.lock} Cargo.lock
substituteInPlace src/meson.build \
--replace-fail \
"'src' / rust_target / meson.project_name()" \
"'src' / '${stdenv.hostPlatform.rust.cargoShortTarget}' / rust_target / meson.project_name()"
'';
env."CC_${stdenv.buildPlatform.rust.rustcTarget}" = rust.envVars.ccForBuild; #< fixes cross build of sql-macros proc-macro
env.CARGO_BUILD_TARGET = stdenv.hostPlatform.rust.rustcTargetSpec;
env.OPENSSL_NO_VENDOR = true; #< speculative, to use the nixos openssl
env.RUSTC_BOOTSTRAP = 1; #< fixes 'error[E0554]: `#![feature]` may not be used on the stable release channel'
# env.LIBSQLITE3_SYS_USE_PKG_CONFIG = 1; #< TODO: use nixos libsqlite instead of pre-packaged one
passthru.updateScript = nix-update-script { };
meta = with lib; {
description = "a mobile-first email client for the GNOME ecosystem";
homepage = "https://gitlab.gnome.org/felinira/envelope/";
@@ -70,4 +84,4 @@ stdenv.mkDerivation {
platforms = platforms.linux;
mainProgram = "envelope";
};
}
})

View File

@@ -11,13 +11,13 @@
}:
stdenvNoCC.mkDerivation (finalAttrs: {
pname = "euicc-manual";
version = "0-unstable-2025-08-14";
version = "0-unstable-2025-09-02";
# XXX: their gitea downloads are broken, so use fetchgit
src = fetchgit {
url = "https://gitea.osmocom.org/sim-card/euicc-manual";
rev = "b9ca1371f6849ec5724f5374a11bb380b7a762a6";
hash = "sha256-4Bveo+4rpwRjmUrneN7ObJekQ3W2woai8Pe8JWlp9QM=";
rev = "e96f0dc86e2d57d9509c0f3d7f26e1a289c80103";
hash = "sha256-S7Y4yNPrpB/8lH7JRl3YKZlNShfoIZzJCS3IdepCqBc=";
};
nativeBuildInputs = [

View File

@@ -33,6 +33,12 @@ stdenv.mkDerivation rec {
hash = "sha256-ALoxT+RLL4omJ7quWDJVdXgevaO8i8q/29FFFudIRV4=";
};
postPatch = ''
substituteInPlace src/meson.build --replace-fail \
"'target' / rust_target / meson.project_name()" \
"'target' / '${stdenv.hostPlatform.rust.cargoShortTarget}' / rust_target / meson.project_name()"
'';
nativeBuildInputs = [
cargo
desktop-file-utils
@@ -49,6 +55,8 @@ stdenv.mkDerivation rec {
openssl
];
env.CARGO_BUILD_TARGET = stdenv.hostPlatform.rust.rustcTargetSpec;
passthru.updateScript = gitUpdater {
rev-prefix = "v";
};

View File

@@ -14,12 +14,12 @@
stdenv.mkDerivation (finalAttrs: {
pname = "lpac";
version = "2.2.1";
version = "2.3.0";
src = fetchFromGitHub {
owner = "estkme-group";
repo = "lpac";
rev = "v${finalAttrs.version}";
hash = "sha256-dxoYuX3dNj4piXQBqU4w1ICeyOGid35c+6ZITQiN6wA=";
hash = "sha256-ALne5sHB6ff7cHAWe0rFwpP/Yz4EhZBiOrgdM2B8+OE=";
};
# options:

View File

@@ -11,16 +11,16 @@
rustPlatform.buildRustPackage {
pname = "mslicer";
version = "0.2.2-unstable-2025-07-18";
version = "0.2.2-unstable-2025-09-14";
src = fetchFromGitHub {
owner = "connorslade";
repo = "mslicer";
rev = "076f2d48dd87ae636678ff21e75b0bed86aa4eae";
hash = "sha256-HNCtY7ZOTEnuJ3ln8P4w6k0yDtacW8OTPOH69ISiYqM=";
rev = "72bc180e054b0a246db3800cafb653b0ec372c03";
hash = "sha256-AEkZ5cQEkre/AZNPOyoHOk8NDSgc5LxsJ1qVZ2d1nAY=";
};
cargoHash = "sha256-A7C+wE5XGGQRTYq85YbVk6iKJETZjA9E08c0tKXdSMw=";
cargoHash = "sha256-Xl6BnLXL+XFo4L0vdSCcB6LJY6eACv2X6TayAD0pDLc=";
buildInputs = [
libglvnd

View File

@@ -14,8 +14,8 @@
mkNixpkgs ? import ./mkNixpkgs.nix {},
}:
mkNixpkgs {
rev = "6207745fb2fb83330b7193fed31337050b075f40";
sha256 = "sha256-NfKklCRVBW979loK3FIXjYYMO2oGfFx/ozfg37oWRcc=";
version = "unstable-2025-08-15";
rev = "9c15b076b331dcc60842a5382dfe0cc4fa30dc5e";
sha256 = "sha256-OIm12uFCc+ttwgRJKbL6KHjMgLO5Fpz2T/YwY1kmG1g=";
version = "unstable-2025-09-18";
branch = "master";
}

View File

@@ -3,12 +3,35 @@
set -eu
branch=$1
usage() {
echo "USAGE: nixpkgs-bootstrap-updater BRANCH"
}
if [ -z "$branch" ]; then
echo "USAGE: nixpkgsUpdater <branch>"
exit 1
fi
branch=
parseArgs() {
while [ "$#" -ne 0 ]; do
local arg=$1
shift
case $arg in
(--help)
usage
exit 0
;;
(*)
if [ -z "$branch" ]; then
branch=$arg
else
usage
exit 1
fi
;;
esac
done
if [ -z "$branch" ]; then
usage
exit 1
fi
}
jsonGetField() {
local json=$1
@@ -16,13 +39,19 @@ jsonGetField() {
echo "$json" | jq --exit-status --raw-output ".$field"
}
main() {
local branch=$1
local prefetchData=$(nix-prefetch-github --json --meta --rev "$branch" NixOS nixpkgs)
local rev=$(jsonGetField "$prefetchData" src.rev)
local nixHash=$(jsonGetField "$prefetchData" src.hash)
local commitDate=$(jsonGetField "$prefetchData" meta.commitDate)
update-source-version "sane.nixpkgs-bootstrap.$branch" "unstable-$commitDate" "$nixHash" \
--rev="$rev" \
--print-changes
}
parseArgs "$@"
set -x
prefetchData=$(nix-prefetch-github --json --meta --rev "$branch" NixOS nixpkgs)
rev=$(jsonGetField "$prefetchData" src.rev)
nixHash=$(jsonGetField "$prefetchData" src.hash)
commitDate=$(jsonGetField "$prefetchData" meta.commitDate)
update-source-version "sane.nixpkgs-bootstrap.$branch" "unstable-$commitDate" "$nixHash" \
--rev="$rev" \
--print-changes
main "$branch"

View File

@@ -30,12 +30,12 @@ let
);
in
[
(fetchpatch' {
name = ''nixos/bind: add an "extraArgs" option'';
# saneCommit = "ab65c92241bd4acab25aad19d0fea4873c1bc3b7";
prUrl = "https://github.com/NixOS/nixpkgs/pull/414825";
hash = "sha256-Hs3uKT3b5D4hkipEgD19j+bv5k63Eba8jMdENaE1Plg=";
})
# (fetchpatch' {
# name = ''nixos/bind: add an "extraArgs" option'';
# # saneCommit = "ab65c92241bd4acab25aad19d0fea4873c1bc3b7";
# prUrl = "https://github.com/NixOS/nixpkgs/pull/414825";
# hash = "sha256-Hs3uKT3b5D4hkipEgD19j+bv5k63Eba8jMdENaE1Plg=";
# })
(fetchpatch' {
name = "zelda64recomp: init at 1.2.0";
@@ -45,51 +45,37 @@ in
# TODO: enable, once i can tolerate a mass rebuild
# (fetchpatch' {
# # 2025-08-06: merged into staging
# name = "v4l-utils: fix cross-compilation";
# prUrl = "https://github.com/NixOS/nixpkgs/pull/429900";
# hash = "sha256-oH9jTG38mWpjwf/LH3MTCrBm2NC4WTRPki2mUhCc5WQ=";
# })
# (fetchpatch' {
# name = "libpcap: enable dbus, rdma, bluetooth features";
# prUrl = "https://github.com/NixOS/nixpkgs/pull/429225";
# hash = "sha256-cALgj+7eXd3H4WAmW6CIcxWRC3D4PoY2PWNsDxK+G9g=";
# })
# (fetchpatch' {
# # 2025-07-15: merged into staging
# name = "treewide: populate arch and platform for more node packages";
# prUrl = "https://github.com/NixOS/nixpkgs/pull/422938";
# hash = "sha256-lN99K0k9dCUBFXc99XB97cZSVDu5A74pHL40vw9cobY=";
# })
# XXX(2025-07-25): master & staging have diverged that the above patch doesn't apply correctly;
# manually recreate the patches against master:
(fetchpatch' {
name = "nodejs: split destCPU into stdenv.$platform.node";
saneCommit = "1fd1d40033deb51bc74ecf11b401cf2ffde5aae3";
hash = "sha256-LGp9HzUINI5iTQ3UtggUNWS4zaYhakUI3OqM6rPiYr0=";
})
(fetchpatch' {
name = "treewide: replace node platform mapping with stdenv.hostPlatform.node.{arch,platform}";
saneCommit = "57bac5daa19c55a547f60271a7b48c59337ec12f";
hash = "sha256-DBenl7O4KuQ1I6jmB66upufYSUdYHRkqRdqRT9stbys=";
})
(fetchpatch' {
name = "buildNpmPackage: push npm_config_* options into npmHooks.npmConfigHook";
saneCommit = "f84ef10710b6634f5bcc4c7bc4764ecfd6e8dec5";
hash = "sha256-BHwtNvSGaqBRzUj0mP3JqAfygxjiKoX3lh4z9+B4UWk=";
})
(fetchpatch' {
name = "pnpm.configHook: set npm_config_{arch,platform}";
saneCommit = "38d2a3d80502ad63686ca886f4438d2942fbddb6";
hash = "sha256-0grq9Os9XD+voupAQuB48WBptH5oM/qYX5iEdmuVqMQ=";
name = "knot-dns, xdp-tools: fix cross compilation";
prUrl = "https://github.com/NixOS/nixpkgs/pull/442827";
hash = "sha256-qWM6+WfjQKCIpTox5WqQUWryGm62uI/RI8pObx98k+I=";
})
(fetchpatch' {
name = "signal-desktop: fix cross compilation";
prUrl = "https://github.com/NixOS/nixpkgs/pull/423089";
# hash = "sha256-609snDT1Ru69ZTWfzu4PnhY0pj3xghPr8w880j7JZ5k=";
hash = "sha256-bVMOanUcYNPf2JbaWS9ga+0jAwuZQSfMKlwtNRp9tYU=";
name = "fractal: fix cross compilation";
prUrl = "https://github.com/NixOS/nixpkgs/pull/437038";
hash = "sha256-B7s2aNVony+G7FW2PaR7FVO7zzWa7SiLONWRGrsXA3A=";
})
(fetchpatch' {
# desko nixpkgs branch: `pr-papers-cross`
name = "papers: fix cross compilation";
prUrl = "https://github.com/NixOS/nixpkgs/pull/437704";
# saneCommit = "eaed8b1530ce9eb9f674677003866d2d793b90fa";
hash = "sha256-G+2I7FMVN7WJio1ufwRZ0F13X/LViNdH1zfR4qqN46Y=";
})
(fetchpatch' {
# desko nixpkgs branch: `pr-snapshot-cross`
name = "snapshot: fix cross compilation";
prUrl = "https://github.com/NixOS/nixpkgs/pull/437708";
# saneCommit = "9bf01eef452d46c2990cdc872017f1015892ea7d";
hash = "sha256-nzqbRP9wHyDnwKyaATZ5mSyn68qgRvs4pEtAkhauiMM=";
})
# (fetchpatch' {
@@ -135,12 +121,6 @@ in
# hash = "sha256-UyZaNNp84zKShuo6zu0nfZ2FygHGcmV63Ww4Y4CtCF0=";
# })
# (fetchpatch' {
# name = "rpm: 4.18.1 -> 4.19.0";
# prUrl = "https://github.com/NixOS/nixpkgs/pull/260558";
# hash = "sha256-kwod+6SbUZechzbmu1D4Hlh6pYiPD18wcqetk0OIOrA=";
# })
# (fetchpatch' {
# # XXX: doesn't cleanly apply; fetch `firefox-pmos-mobile` branch from my git instead
# name = "firefox-pmos-mobile: init at -pmos-2.2.0";

View File

@@ -2,8 +2,8 @@
mkNixpkgs ? import ./mkNixpkgs.nix {},
}:
mkNixpkgs {
rev = "c0e9e1afcfdb5fcd0ae7466a20a9c1ced45ce8a1";
sha256 = "sha256-njAuNOzJKhK4lAuabibqWqsziLZVCMQJmDZRm5MYbbQ=";
version = "unstable-2025-08-15";
rev = "068be7df1fbd1ede053539d9ec438adfd8ebd129";
sha256 = "sha256-8E3S/6XeV82yq1xsruK2Hhp3ZvTB45MHnw4Qqoyh5PM=";
version = "unstable-2025-09-18";
branch = "staging-next";
}

View File

@@ -2,8 +2,8 @@
mkNixpkgs ? import ./mkNixpkgs.nix {},
}:
mkNixpkgs {
rev = "664c1d7072fbafa4c806ac6b22f9a6eee221cfcf";
sha256 = "sha256-Y7DnNNQLKLdQwIwAEyGQPPcqDDLiCZcuJnQ9f/mWigM=";
version = "unstable-2025-08-15";
rev = "c70040a53e83bfe971d79e29d582fdf9f577fbd8";
sha256 = "sha256-yzr4QI1An3T3GIG+XGStXO3JbYKaW+BmG1R9u4/mTPY=";
version = "unstable-2025-09-18";
branch = "staging";
}

View File

@@ -7,8 +7,8 @@ let
src = fetchFromGitHub {
owner = "nix-community";
repo = "nixpkgs-wayland";
rev = "9c452b9ea8e584d5085629e2b76703c94df64b6f";
hash = "sha256-THPRayZls0/4rZSw4R5B7evCbGVr5AN3SQQZSgYpIiA=";
rev = "a6c383565e7a9333e54f032299e7dfd3563cfffd";
hash = "sha256-519sRIp8fPdKWHhzTVkmMMwShoZRQHUYgjdnuxsoJd0=";
};
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-2025-08-14";
version = "0-unstable-2025-09-17";
src = src;
# passthru only nixpkgs-wayland's own packages -- not the whole nixpkgs-with-nixpkgs-wayland-as-overlay:

View File

@@ -39,6 +39,16 @@ stdenvNoCC.mkDerivation {
ln -s ../../Rockchip/rk3399/PinePhonePro/PinePhonePro.conf $out/share/alsa/ucm2/conf.d/simple-card/PinePhonePro.conf
ln -s ../../Allwinner/A64/PinePhone/PinePhone.conf $out/share/alsa/ucm2/conf.d/simple-card/PinePhone.conf
# XXX(2025-09-03): if booted via systemd-boot, the card will be named differently but otherwise function equivalently.
# this is because, if DMI (via EFI) is available, the kernel uses Vendor + Product Name fields to assign the card a long_name,
# instead of the device-tree fields.
# there seems no easy way to change that on the kernel side, nor to configure ALSA UCM to prefer the card short name.
# TODO: consider other ways to configure the card *besides* UCM:
# - smarter in-kernel driver/configs?
# - pipewire / pulseaudio card profiles?
# - particularly, expose the card on the kernel side in such a way that the generic pipewire profiles can configure it.
ln -s PinePhonePro.conf $out/share/alsa/ucm2/conf.d/simple-card/pine64-Pine64PinePhonePro-.conf
runHook postInstall
'';

View File

@@ -20,12 +20,12 @@ stdenv.mkDerivation {
sourceRoot = "clightning-v${clightning.version}/contrib/pyln-spec/bolt7";
nativeBuildInputs = [
python3.pkgs.poetry-core
python3.pkgs.hatchling
python3.pkgs.pypaBuildHook
python3.pkgs.pypaInstallHook
unzip # used by `unpackPhase`
];
propagatedBuildInputs = with python3.pkgs; [
propagatedBuildInputs = [
pyln-proto
];
nativeCheckInputs = [ python3.pkgs.pytestCheckHook ];

View File

@@ -15,7 +15,7 @@ stdenvNoCC.mkDerivation {
sourceRoot = "clightning-v${clightning.version}/contrib/pyln-client";
nativeBuildInputs = [
python3.pkgs.poetry-core
python3.pkgs.hatchling
python3.pkgs.pypaBuildHook
python3.pkgs.pypaInstallHook
unzip # used by `unpackPhase`

View File

@@ -15,7 +15,7 @@ stdenvNoCC.mkDerivation {
sourceRoot = "clightning-v${clightning.version}/contrib/pyln-proto";
nativeBuildInputs = [
python3.pkgs.poetry-core
python3.pkgs.hatchling
python3.pkgs.pypaBuildHook
python3.pkgs.pypaInstallHook
unzip # used by `unpackPhase`

View File

@@ -17,6 +17,7 @@ OPTIONS:
--verbose
"""
import argparse
import ctypes
import logging
import os
@@ -68,14 +69,14 @@ def become_reaper():
assert_prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0)
def kill_all_on_exit(sig: signal.Signals=signal.SIGTERM):
def kill_all_on_exit(killsig: signal.Signals=signal.SIGTERM):
"""
catch when the parent exits, and map that to SIGTERM for this process.
when this process receives SIGTERM, also terminate all descendent processes.
"""
set_pdeathsig()
signal.signal(signal.SIGTERM, lambda _sig=None, _frame=None: kill_all_children(sig))
signal.signal(signal.SIGINT, lambda _sig=None, _frame=None: kill_all_children(sig))
signal.signal(signal.SIGTERM, lambda _sig=None, _frame=None: kill_all_children(killsig))
signal.signal(signal.SIGINT, lambda _sig=None, _frame=None: kill_all_children(killsig))
PGID = None
@@ -112,8 +113,8 @@ def wait_all_children() -> None:
logger.debug(f"child {pid} exited {status}")
child_exited(status)
def kill_all_children(sig: signal.Signals=signal.SIGTERM) -> None:
if sig != signal.SIGKILL: kill_process_group(sig)
def kill_all_children(killsig: signal.Signals=signal.SIGTERM) -> None:
if killsig != signal.SIGKILL: kill_process_group(killsig)
for _ in range(20): # max attempts, arbitrary
children = psutil.Process().children()
@@ -122,23 +123,23 @@ def kill_all_children(sig: signal.Signals=signal.SIGTERM) -> None:
break
for child in children:
child.send_signal(sig)
child.send_signal(killsig)
gone, alive = psutil.wait_procs(children, timeout=0.5)
for p in gone:
child_exited(p._exitcode)
kill_process_group(sig)
kill_process_group(killsig)
def kill_process_group(sig: signal.Signals=signal.SIGTERM) -> None:
def kill_process_group(killsig: signal.Signals=signal.SIGTERM) -> None:
global PGID
if PGID is None:
return
logger.debug("killing process group %d with %d", PGID, sig)
logger.debug("killing process group %d with %d", PGID, killsig)
# reset signal handler to avoid recursing
if sig != signal.SIGKILL: signal.signal(sig, signal.Handlers.SIG_IGN)
os.killpg(PGID, sig)
# if sig != signal.SIGKILL: signal.signal(sig, signal.Handlers.SIG_DFL)
if killsig != signal.SIGKILL: signal.signal(killsig, signal.Handlers.SIG_IGN)
os.killpg(PGID, killsig)
# if killsig != signal.SIGKILL: signal.signal(killsig, signal.Handlers.SIG_DFL)
def do_exec(cli: list[str], killsig: signal.Signals) -> None:
"""
@@ -151,47 +152,41 @@ def do_exec(cli: list[str], killsig: signal.Signals) -> None:
def main():
global PGID
logging.basicConfig()
logging.getLogger().setLevel(logging.INFO)
args = sys.argv[1:]
catch_sigkill = False
descendants = False
killsig_, killsig = None, signal.SIGTERM
use_pgroup = False
verbose = False
while args and args[0].startswith("--"):
flag, args = args[0], args[1:]
if flag == "--catch-sigkill":
catch_sigkill = True
elif flag == "--descendants":
descendants = True
elif flag == "--use-pgroup":
use_pgroup = True
elif flag == "--verbose":
verbose = True
elif flag == "--signal":
killsig_, args = args[0], args[1:]
killsig = getattr(signal, killsig_.upper())
else:
assert False, f"unrecognized argument {flag!r}"
cli = args
parser = argparse.ArgumentParser(description="run a command such that when the caller of sane-die-with-parent exits, that command exits as well")
parser.add_argument("--catch-sigkill", action="store_true")
parser.add_argument("--descendants", action="store_true")
parser.add_argument("--signal", default="SIGTERM")
parser.add_argument("--use-pgroup", action="store_true")
parser.add_argument("--verbose", action="store_true")
parser.add_argument("command", nargs="+", help="command to execute")
if verbose:
args = parser.parse_args()
catch_sigkill = args.catch_sigkill
descendants = args.descendants
killsig = getattr(signal, args.signal.upper())
use_pgroup = args.use_pgroup
command = args.command
if args.verbose:
logging.getLogger().setLevel(logging.DEBUG)
if catch_sigkill:
nested_args = [ sys.argv[0] ]
if descendants:
if args.descendants:
nested_args += [ "--descendants" ]
descendants = True # it's less that we need the outer process to kill its descendants, so much as that it must *exist*
if killsig_:
nested_args += [ "--signal", killsig_ ]
if use_pgroup:
if args.signal:
nested_args += [ "--signal", args.signal ]
if args.use_pgroup:
nested_args += [ "--use-pgroup" ]
use_pgroup = False # doesn't make sense for parent to use pgroups
if verbose:
if args.verbose:
nested_args += [ "--verbose" ]
cli = nested_args + cli
command = nested_args + command
if use_pgroup:
PGID = os.getpid()
@@ -200,11 +195,11 @@ def main():
if descendants:
become_reaper()
do_spawn(cli, killsig)
do_spawn(command, killsig)
kill_all_children()
sys.exit(EXIT_CODE or 0)
else:
do_exec(cli, killsig)
do_exec(command, killsig)
if __name__ == "__main__":
main()

View File

@@ -125,7 +125,7 @@ let
profile = static-nix-shell.mkYsh {
pname = "sane-profile";
srcRoot = ./src;
pkgs = [ "flamegraph" "linuxPackages.perf" ];
pkgs = [ "flamegraph" "perf" ];
};
rcp = static-nix-shell.mkBash {
pname = "sane-rcp";
@@ -166,7 +166,7 @@ let
srcRoot = ./src;
pkgs = [ "nettools" "systemd" ];
};
stop-all-servo = static-nix-shell.mkBash {
stop-all-servo = static-nix-shell.mkYsh {
pname = "sane-stop-all-servo";
srcRoot = ./src;
pkgs = [ "systemd" ];

View File

@@ -33,7 +33,7 @@ class SsdpResponse:
def get_cached_root_devices() -> list[str]:
try:
dev = open("/var/lib/uninsane/upnp.txt", "r").read()
dev = open("/var/lib/dyn-dns/upnp.txt", "r").read()
except IOError:
return []
else:

View File

@@ -19,34 +19,61 @@
# configurables:
var deadlines = "$[ENV.HOME]/knowledge/planner/deadlines.tsv"
if ! test -f "$deadlines" {
echo "WARNING: $deadlines sane-deadlines file not found"
exit 1
proc usage {
echo "sane-deadlines:"
echo " print any upcoming deadlines from $deadlines"
}
var now=$(date +%s)
for line in ($(cat "$deadlines") => split(u'\n')) {
if (line === "" or line => startsWith("#")) {
# ignore comments
continue
}
# parse normal line
var deadline_field=$(echo "$line" | cut -f 1)
var threshold_field=$(echo "$line" | cut -f 2)
var description_field=$(echo "$line" | cut -f 3)
# normalize dates into seconds since unix epoch
var deadline=$(date -d "$deadline_field" +%s)
var threshold=$(echo "$threshold_field" | sed 's/d/day /g')
var birthtime=$(date -d "$deadline_field - $threshold" +%s)
# show the event iff it's near
# 86400 = 24*60*60 = # of seconds in a day
# 10080 = 24*60*7 = # of seconds between UTC and UTC-7
# 11520 = 24*60*8 = # of seconds between UTC and UTC-8
if (now >= birthtime) {
var days_until = int((deadline - now + 86399) / 86400)
echo "in $days_until day(s): $description_field"
func parseArgs(args) {
for arg in (args) {
case (arg) {
("--help") {
usage
exit 0
}
(else) {
usage
exit 1
}
}
}
}
proc main {
if ! test -f "$deadlines" {
echo "WARNING: $deadlines sane-deadlines file not found"
exit 1
}
var now=$(date +%s)
for line in ($(cat "$deadlines") => split(u'\n')) {
if (line === "" or line => startsWith("#")) {
# ignore comments
continue
}
# parse normal line
var deadline_field=$(echo "$line" | cut -f 1)
var threshold_field=$(echo "$line" | cut -f 2)
var description_field=$(echo "$line" | cut -f 3)
# normalize dates into seconds since unix epoch
var deadline=$(date -d "$deadline_field" +%s)
var threshold=$(echo "$threshold_field" | sed 's/d/day /g')
var birthtime=$(date -d "$deadline_field - $threshold" +%s)
# show the event iff it's near
# 86400 = 24*60*60 = # of seconds in a day
# 10080 = 24*60*7 = # of seconds between UTC and UTC-7
# 11520 = 24*60*8 = # of seconds between UTC and UTC-8
if (now >= birthtime) {
var days_until = int((deadline - now + 86399) / 86400)
echo "in $days_until day(s): $description_field"
}
}
}
if is-main {
call parseArgs(ARGV)
main
}

View File

@@ -3,10 +3,12 @@
# watches PWD for any changes underneath it and re-runs `cargo build --a>
# optionally, provide your own build command as the first argument
set -eu
external_cmd="cargo build --all"
if [ -n "$1" ]
then
external_cmd=$1
external_cmd=$1
fi
# run this once before starting the inotify
@@ -26,6 +28,6 @@ inotifywait --monitor --recursive \
-e modify ./ |
while read -r date time dir file
do
tput reset
$external_cmd
tput reset
$external_cmd
done

View File

@@ -3,9 +3,40 @@
# find where a package stores its dotfiles/dotdir
# e.g. `sane-find-dotfiles foo` might print `/home/colin/.foo`, `/home/colin/.local/share/foo`, etc.
find ~/ ~/.config/ ~/.cache/ ~/.local/share/ -maxdepth 1 -iname "*$1*" -print
find ~/.local/share/*/ -maxdepth 1 -iname "*$1*" -print
find ~/.local/state/*/ -maxdepth 1 -iname "*$1*" -print
find ~/.config/*/ -maxdepth 1 -iname "*$1*" -print
find ~/.cache/*/ -maxdepth 1 -iname "*$1*" -print
set -eu
usage() {
echo "sane-find-dotfiles NAME..."
echo " find files matching NAME (case-insensitive) under common dot directories"
echo " like ~/.config, ~/.local/share, etc"
}
names=()
parseArgs() {
while [ "$#" -ne 0 ]; do
local arg=$1
shift
case $arg in
(--help)
usage
exit 0
;;
(*)
names+=("$arg")
;;
esac
done
}
main() {
for name in "${names[@]}"; do
find ~/ ~/.config/ ~/.cache/ ~/.local/share/ -maxdepth 1 -iname "*${name}*" -print
find ~/.local/share/*/ -maxdepth 1 -iname "*${name}*" -print
find ~/.local/state/*/ -maxdepth 1 -iname "*${name}*" -print
find ~/.config/*/ -maxdepth 1 -iname "*${name}*" -print
find ~/.cache/*/ -maxdepth 1 -iname "*${name}*" -print
done
}
parseArgs "$@"
main

View File

@@ -2,7 +2,7 @@
#!nix-shell -i python3 -p inetutils -p miniupnpc -p python3 -p sane-scripts.lib.ssdp
# vim: set filetype=python :
'''
USAGE = '''
USAGE: sane-ip-port-forward [options] [proto:port[:desc]]*
options:
@@ -25,13 +25,15 @@ import sys
from sane_ssdp import get_any_wan, forward_port
class BadCliArgs(Exception):
class Usage(Exception):
def __init__(self, msg: str = None):
helpstr = __doc__.format(DEFAULT_LEASE_SEC=DEFAULT_LEASE_SEC).strip()
helpstr = USAGE.format(DEFAULT_LEASE_SEC=DEFAULT_LEASE_SEC).strip()
if msg:
super().__init__(f"{msg}\n\n{helpstr}")
self.exit_ = 1
else:
super().__init__(helpstr)
self.exit_ = 0
def try_parse_port(s: str):
"""
@@ -73,8 +75,8 @@ def parse_args(argv: "List[str]") -> "List[('udp'|'tcp', port: int, description:
unparsed = sys.argv[1:][::-1]
while unparsed:
arg = unparsed.pop()
if arg == "-h":
raise BadCliArgs()
if arg in ["-h", "--help"]:
raise Usage()
if arg == "-v":
logging.getLogger().setLevel(logging.INFO)
elif arg == "-vv":
@@ -84,11 +86,11 @@ def parse_args(argv: "List[str]") -> "List[('udp'|'tcp', port: int, description:
try:
duration = int(d)
except Exception:
raise BadCliArgs(f"invalid CLI argument: -d {d!r}")
raise Usage(f"invalid CLI argument: -d {d!r}")
elif try_parse_port(arg):
forwards.append(try_parse_port(arg))
else:
raise BadCliArgs(f"invalid CLI argument: {arg!r}")
raise Usage(f"invalid CLI argument: {arg!r}")
return forwards, duration
if __name__ == '__main__':
@@ -96,9 +98,9 @@ if __name__ == '__main__':
try:
forwards, duration = parse_args(sys.argv)
except BadCliArgs as e:
except Usage as e:
print(e)
sys.exit(1)
sys.exit(e.exit_)
root_device, lan, _wan = get_any_wan(cached=True)
for (proto, port, reason) in forwards:

View File

@@ -1,36 +1,69 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p bash -p util-linux
# unlock the private store, run some command, and then re-lock the store
# N.B.: `mount`ing requires elevated privileges (cap_sys_admin).
# for this reason, it's expected that this script is run from within a mount namespace, not directly.
# this also has the side effect that parent/sibling processes don't see our mount in their namespace.
# i.e. the real /mnt/persist/private path is never modified.
set -eu
mntpoint=/mnt/persist/private
if mountpoint -q "$mntpoint"; then
# don't mount/unmount if it already exists (re-entrancy)
mntpoint=
fi
usage() {
echo 'sane-private-do [COMMAND ...]'
echo
echo 'unlock the private store, run some command, and then re-lock the store'
echo 'N.B.: `mount`ing requires elevated privileges (cap_sys_admin).'
echo " for this reason, it's expected that this script is run from within a mount namespace, not directly."
echo " this also has the side effect that parent/sibling processes don't see our mount in their namespace."
echo ' i.e. the real /mnt/persist/private path is never modified.'
echo
echo "if COMMAND is empty, launches the user's default shell"
}
external_cmd=("$@")
if [ -z "$external_cmd" ]; then
if [ -n "$SHELL" ]; then
external_cmd=("$SHELL" "-i")
else
external_cmd=("/bin/sh" "-i")
cmd=()
parseArgs() {
while [ "$#" -ne 0 ]; do
local arg=$1
shift
if (( ${#cmd[@]} )); then
# all remaining arguments are to be forwarded to the subcommand
cmd+=("$arg")
else
case $arg in
(--help)
usage
exit 0
;;
(*)
cmd=("$arg")
;;
esac
fi
done
}
main() {
local mntpoint=/mnt/persist/private
if mountpoint -q "$mntpoint"; then
# don't mount/unmount if it already exists (re-entrancy)
mntpoint=
fi
fi
if [ -n "$mntpoint" ]; then
mount "$mntpoint"
fi
local external_cmd=("${cmd[@]}")
if ! (( ${#x[@]} )); then
external_cmd=("${SHELL:-/bin/sh}" "-i")
fi
"${external_cmd[@]}"
RC=$?
if [ -n "$mntpoint" ]; then
mount "$mntpoint"
fi
if [ -n "$mntpoint" ]; then
umount "$mntpoint"
fi
set +e
"${external_cmd[@]}"
local RC=$?
set -e
exit "$RC"
if [ -n "$mntpoint" ]; then
umount "$mntpoint"
fi
exit "$RC"
}
parseArgs "$@"
main

View File

@@ -3,19 +3,46 @@
umask 027
if ! systemctl is-active private-storage.target > /dev/null; then
echo "unable to access encrypted data store."
echo "unlock it now or cancel with Ctrl+C."
fi
usage() {
echo "sane-private-unlock:"
echo " unlock the user's private data store (roughly equivalent to an encyrpted home directory)"
echo " or does nothing if already unlocked."
}
while ! systemctl is-active private-storage.target > /dev/null; do
# see: <https://stackoverflow.com/a/2654096>
IFS= read -s -r -p "password: " line
echo
if [[ "$?" -eq 0 && -n "$line" ]]; then
echo -n "$line" > /run/gocryptfs/private.key
# give time for the store to be unlocked
timeout 3s systemctl start private-storage.target
parseArgs() {
while [ $# -ne 0 ]; do
local arg="$1"
shift
case "$arg" in
(--help)
usage
exit 0
;;
(*)
usage
exit 1
;;
esac
done
}
main() {
if ! systemctl is-active private-storage.target > /dev/null; then
echo "unable to access encrypted data store."
echo "unlock it now or cancel with Ctrl+C."
fi
done
while ! systemctl is-active private-storage.target > /dev/null; do
# see: <https://stackoverflow.com/a/2654096>
IFS= read -s -r -p "password: " line
echo
if [[ "$?" -eq 0 && -n "$line" ]]; then
echo -n "$line" > /run/gocryptfs/private.key
# give time for the store to be unlocked
timeout 3s systemctl start private-storage.target
fi
done
}
parseArgs "$@"
main

View File

@@ -6,14 +6,48 @@
set -eu
host=$1
passwd=$(sane-secrets-dump --field password "$host")
usage() {
echo "sane-private-unlock-remote HOST"
echo " ssh's into HOST and unlocks its encrypted data store (e.g. encrypted home directory)"
echo " this is non-interactive, and only works so long as the machine running this script"
echo " has HOST's key in its own keyring"
}
if [ -z "$passwd" ]; then
echo "failed to decode password" | tee /dev/stderr
exit 1
fi
host=
parseArgs() {
while [ "$#" -ne 0 ]; do
local arg=$1
shift
case $arg in
(--help)
usage
exit 0
;;
(*)
if [ -z "$host" ]; then
host=$arg
else
usage
exit 1
fi
;;
esac
done
}
echo "attempting to mount private store on $host"
echo "$passwd" | timeout 30 ssh "$host" 'if ! test -f /mnt/persist/private/init; then cat /dev/stdin > /run/gocryptfs/private.key; fi'
echo "mount succeeded"
main() {
local host="$1"
local passwd=$(sane-secrets-dump --field password "$host")
if [ -z "$passwd" ]; then
echo "failed to decode password" | tee /dev/stderr
exit 1
fi
echo "attempting to mount private store on $host"
echo "$passwd" | timeout 30 ssh "$host" 'if ! test -f /mnt/persist/private/init; then cat /dev/stdin > /run/gocryptfs/private.key; fi'
echo "mount succeeded"
}
parseArgs "$@"
main "$host"

View File

@@ -1,13 +1,48 @@
#!/usr/bin/env nix-shell
#!nix-shell -i ysh -p flamegraph -p linuxPackages.perf -p oils-for-unix
#!nix-shell -i ysh -p flamegraph -p oils-for-unix -p perf
# use like `sane-profile some-command --which-takes-a flag`.
# will render a .html file showing the hot-path functions inside the command.
var cmd = ARGV
var tmpdir = "/tmp"
perf record -F 9000 -e cycles:u -g -o "$tmpdir/perf.data" -- @cmd
perf script --input "$tmpdir/perf.data" > "$tmpdir/perf.script"
stackcollapse-perf.pl "$tmpdir/perf.script" | flamegraph.pl --width 2000 > "$tmpdir/flamegraph.html"
proc usage {
echo "sane-profile CMD..."
echo " invoke CMD under a profiler and render a HTML flamegraph"
}
echo "flame graph available at $tmpdir/flamegraph.html"
func parseArgs(...args) {
var forward_all = false
var cmd = []
for arg in (args) {
if (forward_all) {
call cmd->append(arg)
} else {
case (arg) {
("--help") {
usage
exit 0
}
("--") {
setvar forward_all = true
}
(else) {
call cmd->append(arg)
}
}
}
}
return (cmd)
}
proc main(; cmd) {
var tmpdir = "/tmp"
perf record -F 9000 -e cycles:u -g -o "$tmpdir/perf.data" -- @cmd
perf script --input "$tmpdir/perf.data" > "$tmpdir/perf.script"
stackcollapse-perf.pl "$tmpdir/perf.script" | flamegraph.pl --width 2000 > "$tmpdir/flamegraph.html"
echo "flame graph available at $tmpdir/flamegraph.html"
}
if is-main {
var cmd = parseArgs(...ARGV)
call main(cmd)
}

View File

@@ -1,19 +1,52 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p bash -p nettools -p systemd
target="$1"
shift
# TODO: merge with sane-shutdown
host="$(hostname)"
if [ "$host" = "$target" ]
then
# N.B.: anything other than just `reboot` with no args requires `sudo` privileges (to write to /run/systemd/).
# `systemctl reboot -i` tells systemd to ignore inhibitors (i.e. other users logged in).
timeout 5 reboot "$@" || \
timeout 5 systemctl reboot -i "$@" || \
(sync && reboot --force --force "$@") #< XXX: requires root
else
echo "WRONG MACHINE. you're on $host."
exit 1
fi
set -eu
usage() {
echo "sane-reboot HOST"
echo " reboot the current machine, so long as its hostname equals HOST"
}
target=
parseArgs() {
while [ "$#" -ne 0 ]; do
local arg=$1
shift
case $arg in
(--help)
usage
exit 0
;;
(*)
if [ -z "$target" ]; then
target=$arg
else
usage
exit 1
fi
;;
esac
done
}
main() {
local target="$1"
host="$(hostname)"
if [ "$host" = "$target" ]
then
# N.B.: anything other than just `reboot` with no args requires `sudo` privileges (to write to /run/systemd/).
# `systemctl reboot -i` tells systemd to ignore inhibitors (i.e. other users logged in).
timeout 5 reboot "$@" || \
timeout 5 systemctl reboot -i "$@" || \
(sync && reboot --force --force "$@") #< XXX: requires root
else
echo "WRONG MACHINE. you're on $host."
exit 1
fi
}
parseArgs "$@"
main "$target"

View File

@@ -5,20 +5,44 @@
# some of this is documented here:
# - <https://nixos.wiki/wiki/Storage_optimization>
if [ "$(id -u)" -ne 0 ]; then
echo 'must be run as root!'
fi
set -eu
set -xeu
usage() {
echo "sane-reclaim-disk-space"
echo " equivalent to nix-collect-garbage --delete-older-than 30d"
}
# scan the store and hard-link identical files
# nix-store --optimise
parseArgs() {
while [ "$#" -ne 0 ]; do
local arg=$1
shift
case $arg in
(--help)
usage
exit 0
;;
(*)
usage
exit 1
;;
esac
done
}
nix-collect-garbage --delete-older-than 30d
main() {
if [ "$(id -u)" -ne 0 ]; then
echo 'must be run as root!'
fi
set +x
# scan the store and hard-link identical files
# nix-store --optimise
(set -x ; nix-collect-garbage --delete-older-than 30d)
echo 'to free more space (e.g. boot space), manually manage the profiles'
echo '- nix-env --profile /nix/var/nix/profiles/system --list-generations'
echo '- nix-env --profile /nix/var/nix/profiles/system --delete-generations {first..last}'
echo '- sudo sane-reclaim-boot-space'
echo 'to free more space (e.g. boot space), manually manage the profiles'
echo '- nix-env --profile /nix/var/nix/profiles/system --list-generations'
echo '- nix-env --profile /nix/var/nix/profiles/system --delete-generations {first..last}'
echo '- sudo sane-reclaim-boot-space'
}
parseArgs "$@"
main

View File

@@ -2,13 +2,29 @@
#!nix-shell -i bash -p bash -p coreutils-full -p openssh -p ssh-to-age
# unlocks the SOPS store (i.e. populate a SOPS key from the user's SSH key)
if test -f ~/.config/sops/age/keys.txt; then
exit
fi
set -eu
set -x
usage() {
echo "sane-secrets-unlock"
echo " unlock the SOPS secret store, i.e. make secrets in ~/knowledge readable"
}
mkdir -p ~/.config/sops/age
parseArgs() {
while [ "$#" -ne 0 ]; do
local arg=$1
shift
case $arg in
(--help)
usage
exit 0
;;
(*)
usage
exit 1
;;
esac
done
}
tryUnlock() {
# unlock the SSH key
@@ -19,18 +35,31 @@ tryUnlock() {
chmod 600 ~/.config/sops/age/keys.txt
}
# there are some dependencies not properly conveyed within the services which may invoke sane-secrets-unlock.
# so show some grace if invoked e.g. before ~/.ssh/ symlinks have been created
tryUnlock \
|| (sleep 1 && tryUnlock) \
|| (sleep 1 && tryUnlock) \
|| (sleep 2 && tryUnlock) \
|| (sleep 4 && tryUnlock) \
|| (sleep 4 && tryUnlock) \
|| (sleep 4 && tryUnlock) \
main() {
if test -f ~/.config/sops/age/keys.txt; then
exit
fi
# remove the unlocked SSH key
rm -f ~/.config/sops/age/id_ed25519
set -x
# present the pubkey for convenience (e.g. if this sops key is new)
echo pubkey: $(cat ~/.ssh/id_ed25519.pub | ssh-to-age)
mkdir -p ~/.config/sops/age
# there are some dependencies not properly conveyed within the services which may invoke sane-secrets-unlock.
# so show some grace if invoked e.g. before ~/.ssh/ symlinks have been created
tryUnlock \
|| (sleep 1 && tryUnlock) \
|| (sleep 1 && tryUnlock) \
|| (sleep 2 && tryUnlock) \
|| (sleep 4 && tryUnlock) \
|| (sleep 4 && tryUnlock) \
|| (sleep 4 && tryUnlock) \
# remove the unlocked SSH key
rm -f ~/.config/sops/age/id_ed25519
# present the pubkey for convenience (e.g. if this sops key is new)
echo pubkey: $(cat ~/.ssh/id_ed25519.pub | ssh-to-age)
}
parseArgs "$@"
main

View File

@@ -1,8 +1,38 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p bash -p coreutils-full -p findutils -p sops
# after modifying .sops.yaml, run this to re-encode all secrets to the new keys
# pass the base directory (under which *everything* is a secret) as argument
for i in $(find "$1" -print)
do
yes | sops updatekeys "$i"
done
usage() {
echo "sane-secrets-update-keys DIRECTORY..."
echo
echo "after modifying .sops.yaml, run this to re-encode all secrets to the new keys"
echo "pass the base directory (under which *everything* is a secret) as argument(s)"
}
paths=()
parseArgs() {
while [ "$#" -ne 0 ]; do
local arg=$1
shift
case $arg in
(--help)
usage
exit 0
;;
(*)
paths+=("$arg")
;;
esac
done
}
main() {
for base in "${paths[@]}"; do
for i in $(find "$base" -print)
do
yes | sops updatekeys "$i"
done
done
}
parseArgs "$@"
main

View File

@@ -1,15 +1,46 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p bash -p nettools -p systemd
target="$1"
shift
set -eu
host="$(hostname)"
if [ "$host" = "$target" ]
then
shutdown now "$@"
else
echo "WRONG MACHINE. you're on $host."
exit 1
fi
usage() {
echo "sane-shutdown HOST"
echo " shutdown the current machine, so long as its hostname equals HOST"
}
target=
parseArgs() {
while [ "$#" -ne 0 ]; do
local arg=$1
shift
case $arg in
(--help)
usage
exit 0
;;
(*)
if [ -z "$target" ]; then
target=$arg
else
usage
exit 1
fi
;;
esac
done
}
main() {
local target="$1"
local host="$(hostname)"
if [ "$host" = "$target" ]
then
shutdown now "$@"
else
echo "WRONG MACHINE. you're on $host."
exit 1
fi
}
parseArgs "$@"
main "$target"

View File

@@ -1,62 +1,91 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p bash -p systemd
systemctl stop \
bitcoind-mainnet \
coturn \
dovecot \
duplicity \
duplicity.timer \
dyn-dns \
dyn-dns.timer \
ejabberd \
gitea \
goaccess \
hickory-dns-doof \
hickory-dns-hn \
hickory-dns-hn-resolver \
hickory-dns-lan \
hickory-dns-wan \
i2p \
jackett \
jellyfin \
kiwix-serve \
komga \
lemmy \
lemmy-ui \
matrix-appservice-irc\
matrix-synapse \
mautrix-signal \
monero \
murmur \
mx-puppet-discord \
navidrome \
nginx \
nix-serve \
ntfy-sh \
opendkin \
phpfpm-freshrss \
pict-rs \
pleroma \
postfix \
postgresql \
prosody \
sftpgo \
signald \
slskd \
smartd \
systemctl \
tor \
transmission \
wireguard-wg-ovpns \
ntfy-waiter-0 \
ntfy-waiter-1 \
ntfy-waiter-2 \
ntfy-waiter-3 \
ntfy-waiter-4 \
ntfy-waiter-5 \
ntfy-waiter-6 \
ntfy-waiter-7 \
ntfy-waiter-8 \
ntfy-waiter-9 \
#!nix-shell -i ysh -p oils-for-unix -p systemd
# TODO: stop the freshrss timer
proc usage {
echo "sane-stop-all-servo:"
echo " stop all non-critical services"
echo " this is especially useful to get the system into a low-activity state for debugging"
}
func parseArgs(args) {
for arg in (ARGV) {
case (arg) {
("--help") {
usage
exit 0
}
(else) {
usage
exit 1
}
}
}
}
proc main {
systemctl stop \
bitcoind-mainnet \
coturn \
dovecot \
duplicity \
duplicity.timer \
dyn-dns \
dyn-dns.timer \
ejabberd \
gitea \
goaccess \
hickory-dns-doof \
hickory-dns-hn \
hickory-dns-hn-resolver \
hickory-dns-lan \
hickory-dns-wan \
i2p \
jackett \
jellyfin \
kiwix-serve \
komga \
lemmy \
lemmy-ui \
matrix-appservice-irc\
matrix-synapse \
mautrix-signal \
monero \
murmur \
mx-puppet-discord \
navidrome \
nginx \
nix-serve \
ntfy-sh \
opendkin \
phpfpm-freshrss \
pict-rs \
pleroma \
postfix \
postgresql \
prosody \
sftpgo \
signald \
slskd \
smartd \
systemctl \
tor \
transmission \
wireguard-wg-ovpns \
ntfy-waiter-0 \
ntfy-waiter-1 \
ntfy-waiter-2 \
ntfy-waiter-3 \
ntfy-waiter-4 \
ntfy-waiter-5 \
ntfy-waiter-6 \
ntfy-waiter-7 \
ntfy-waiter-8 \
ntfy-waiter-9 \
# TODO: stop the freshrss timer
}
if is-main {
call parseArgs(ARGV)
main
}

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