forked from colin/nix-files
Compare commits
186 Commits
patch-gite
...
master
Author | SHA1 | Date | |
---|---|---|---|
80e6d9743f | |||
180829b4da | |||
8eb332140e | |||
e551ab0eba | |||
52b58b1f63 | |||
3306f4dda5 | |||
7e14b86ded | |||
f31067a18a | |||
a769ab6af3 | |||
4ce58798b5 | |||
b8ce3e570f | |||
daa3d68f29 | |||
4951db607c | |||
f7b6981135 | |||
3fe684f2c1 | |||
039c561ae6 | |||
619de95e58 | |||
4228a939c5 | |||
04ce5789d5 | |||
14d48255a5 | |||
26dc346cb3 | |||
46f11b3d7e | |||
cdad23d7c1 | |||
71bd25063f | |||
adf23c093c | |||
232f0a7958 | |||
9fad26b20d | |||
99bdac91c3 | |||
d80832b3c8 | |||
13b1c81090 | |||
1413fac58c | |||
278d01dd8a | |||
0eff2ff545 | |||
8bc7afede6 | |||
fb49022b14 | |||
bd0f6e9cf6 | |||
3b5a795535 | |||
1b48de661a | |||
f492afa35a | |||
75f27f1496 | |||
598bf2e5be | |||
474f1a9e3c | |||
39002c3700 | |||
c5ccbf1405 | |||
2f0a2cc27b | |||
c5dc5a7561 | |||
2def3204d0 | |||
3d37f743e7 | |||
6acd76fbb5 | |||
d9f4934d97 | |||
18a045c000 | |||
eb4b4b31cf | |||
5d2a73afb5 | |||
f2d5bfcf6b | |||
dd4131ba16 | |||
9b1fbebc3c | |||
9cbe47cee0 | |||
f95d5dd501 | |||
49250d9901 | |||
76744c427a | |||
91e821c027 | |||
72c354ca2c | |||
c24f855502 | |||
4cfb374a5d | |||
e422552633 | |||
a4a1a5afce | |||
3826a103df | |||
a12593b608 | |||
815c16a6d1 | |||
b46a294e56 | |||
8a90311ea6 | |||
7d18ebd6da | |||
80a528b790 | |||
212e81d521 | |||
6c137994e9 | |||
7a5df886a1 | |||
6393ec68da | |||
1c4cd0aad5 | |||
acbbed7149 | |||
71a515f235 | |||
7c14f46a4f | |||
10cabf5056 | |||
47ac5ca23c | |||
52b5626903 | |||
f645005b45 | |||
9979289be1 | |||
a1396fb273 | |||
1c38797bad | |||
c66764fba5 | |||
7e7cac8ecd | |||
e7a1ff8b3d | |||
e43926eb25 | |||
4dbce1baae | |||
55979b7e4e | |||
fadcc1a842 | |||
5526bba05c | |||
26412ee801 | |||
3cdbd133f6 | |||
bd8c7c971a | |||
4c4a24d08d | |||
86b037c835 | |||
32ea5ad846 | |||
3a1eec4308 | |||
e81a2498b7 | |||
5f8cfcb7e9 | |||
13205492b4 | |||
176ef307b2 | |||
11293a4d0a | |||
a34449faed | |||
4f3b1dd361 | |||
f7335634a1 | |||
faa8a6968d | |||
fd54b70b36 | |||
05b864143e | |||
d9b82e8b1e | |||
ad559acbd9 | |||
4d4b28027c | |||
da52f21da2 | |||
fea85f438b | |||
13db8bec76 | |||
01e3ace398 | |||
745ee33394 | |||
1c5c9b80eb | |||
94289c2253 | |||
0c443fae25 | |||
a490a74390 | |||
2e71e06c05 | |||
203832b5a8 | |||
1204f4db69 | |||
a4b114fce2 | |||
7e17eb4056 | |||
259d980a60 | |||
5488486944 | |||
969717b1fe | |||
7391e34f77 | |||
7f45077485 | |||
ceb7ccbc6d | |||
9d63ec5dd2 | |||
7ce93eae96 | |||
1277c73304 | |||
0550498cd1 | |||
023396a41e | |||
ebb335ef4c | |||
fbb0046dda | |||
59d4197bf5 | |||
90e4e20274 | |||
7ecd368e20 | |||
79fc30da0e | |||
23f3647cc5 | |||
ad4910366d | |||
609becadfe | |||
3acabe60b6 | |||
87ec095b8a | |||
8831d8d1ac | |||
8b333a8887 | |||
028d903e9c | |||
dfed5f070b | |||
b29ee5ac03 | |||
e700ff392f | |||
91578c0b78 | |||
b35656c9ae | |||
726281a6dd | |||
f305027678 | |||
2b69c07d12 | |||
544b1e58e0 | |||
34c2d4f66f | |||
4addf857b7 | |||
a3f6c148d3 | |||
43a0abd68f | |||
b3c4e96d6e | |||
ade5ce5339 | |||
e543034fcb | |||
b5d96ed17b | |||
003ce70cd7 | |||
04f6964711 | |||
63cf19f839 | |||
806a1aa294 | |||
35a023f449 | |||
f0aec4416c | |||
e0bb1b7c62 | |||
9847e0171c | |||
03a1638628 | |||
f7327bef3e | |||
47fb8296db | |||
b409fbb5f7 | |||
84092395f4 |
5
TODO.md
5
TODO.md
@@ -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
|
||||
|
@@ -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!
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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";
|
||||
|
@@ -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;
|
||||
};
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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";
|
||||
};
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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";
|
||||
};
|
||||
}
|
||||
|
@@ -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";
|
||||
};
|
||||
}
|
||||
|
@@ -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" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@@ -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;
|
||||
};
|
||||
}
|
||||
|
@@ -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 }
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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" {}))
|
||||
]
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -25,6 +25,7 @@
|
||||
imports = [
|
||||
./bind.nix
|
||||
./hickory-dns.nix
|
||||
./kresd.nix
|
||||
./unbound.nix
|
||||
];
|
||||
|
||||
|
60
hosts/common/net/dns/kresd.nix
Normal file
60
hosts/common/net/dns/kresd.nix
Normal 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
|
||||
'';
|
||||
};
|
||||
}
|
@@ -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;
|
||||
|
@@ -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]]
|
||||
|
@@ -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;
|
||||
|
@@ -216,6 +216,7 @@
|
||||
./tcpdump.nix
|
||||
./tor-browser.nix
|
||||
./tuba.nix
|
||||
./u-boot-pinephone-pro
|
||||
./unl0kr
|
||||
./uptime.nix
|
||||
./v4l-utils.nix
|
||||
|
@@ -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
|
||||
|
@@ -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";
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
};
|
||||
};
|
||||
|
@@ -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]
|
||||
|
||||
|
@@ -36,7 +36,8 @@
|
||||
sandbox.extraPaths = [
|
||||
"/boot"
|
||||
"/mnt/desko"
|
||||
"/mnt/lappy"
|
||||
"/mnt/flowy"
|
||||
# "/mnt/lappy"
|
||||
"/mnt/moby"
|
||||
"/mnt/servo"
|
||||
# "nix"
|
||||
|
@@ -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";
|
||||
|
@@ -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=
|
||||
|
@@ -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"
|
||||
];
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
};
|
||||
};
|
||||
|
@@ -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
|
||||
|
@@ -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 [
|
||||
|
@@ -23,6 +23,7 @@
|
||||
'';
|
||||
});
|
||||
sandbox.extraHomePaths = [
|
||||
".config/nvim"
|
||||
".config/sops"
|
||||
"nixos"
|
||||
# TODO: sops should only need access to knowledge/secrets,
|
||||
|
@@ -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
|
||||
|
21
hosts/common/programs/u-boot-pinephone-pro/default.nix
Normal file
21
hosts/common/programs/u-boot-pinephone-pro/default.nix
Normal 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"
|
||||
];
|
||||
}
|
25
hosts/common/programs/u-boot-pinephone-pro/install-u-boot
Normal file
25
hosts/common/programs/u-boot-pinephone-pro/install-u-boot
Normal 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
|
@@ -65,7 +65,8 @@
|
||||
sandbox.extraPaths = [
|
||||
"/boot"
|
||||
"/mnt/desko"
|
||||
"/mnt/lappy"
|
||||
"/mnt/flowy"
|
||||
# "/mnt/lappy"
|
||||
"/mnt/moby"
|
||||
"/mnt/servo"
|
||||
# "nix"
|
||||
|
@@ -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;
|
||||
}
|
||||
];
|
||||
|
@@ -116,9 +116,9 @@ in
|
||||
# see: `man logind.conf`
|
||||
# don’t 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 = ''
|
||||
|
@@ -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;
|
||||
};
|
||||
}
|
||||
];
|
||||
|
@@ -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;
|
||||
|
@@ -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`?
|
@@ -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>;
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
};
|
||||
}
|
||||
|
@@ -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 ];
|
||||
};
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ let
|
||||
pname = "ip";
|
||||
pkgs = [ "iproute2" "systemdMinimal" ];
|
||||
srcRoot = ./.;
|
||||
doInstallCheck = false; #< doesn't implement required `--help` command
|
||||
};
|
||||
in symlinkJoin {
|
||||
name = "tailscale-iproute2";
|
||||
|
@@ -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}"
|
||||
)
|
||||
];
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
|
@@ -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[@]}"
|
||||
|
@@ -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
|
||||
}
|
@@ -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
|
||||
'')
|
||||
]
|
||||
])
|
||||
;
|
||||
};
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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";
|
||||
|
@@ -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";
|
||||
|
@@ -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;
|
||||
# };
|
||||
}
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
14
pkgs/by-name/bootpart-edk2-rpi/config.txt
Normal file
14
pkgs/by-name/bootpart-edk2-rpi/config.txt
Normal 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
|
30
pkgs/by-name/bootpart-edk2-rpi/package.nix
Normal file
30
pkgs/by-name/bootpart-edk2-rpi/package.nix
Normal 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
|
||||
''
|
133
pkgs/by-name/edk2-rpi4/package.nix
Normal file
133
pkgs/by-name/edk2-rpi4/package.nix
Normal 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" ];
|
||||
};
|
||||
}
|
@@ -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";
|
||||
};
|
||||
}
|
||||
})
|
||||
|
@@ -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 = [
|
||||
|
@@ -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";
|
||||
};
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
|
@@ -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";
|
||||
}
|
||||
|
@@ -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"
|
||||
|
@@ -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";
|
||||
|
@@ -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";
|
||||
}
|
||||
|
@@ -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";
|
||||
}
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
'';
|
||||
|
||||
|
@@ -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 ];
|
||||
|
@@ -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`
|
||||
|
@@ -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`
|
||||
|
@@ -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()
|
||||
|
@@ -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" ];
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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"
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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"
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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"
|
||||
|
@@ -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
Reference in New Issue
Block a user