Compare commits
97 Commits
staging/li
...
staging/ni
Author | SHA1 | Date | |
---|---|---|---|
27d2f756d2 | |||
3ab33956e4 | |||
0b71712208 | |||
f31619d9e9 | |||
61838a589f | |||
c10c887650 | |||
6df61525a1 | |||
e5ce7c02ef | |||
88e5efd1f3 | |||
e9200ffcdf | |||
ab78a36354 | |||
c92f216a5b | |||
eacd3c88d1 | |||
487fbf2236 | |||
97f93e8ec0 | |||
e1eac4ae46 | |||
44d0b4efd4 | |||
9ab85167c3 | |||
9730659f32 | |||
b45981e870 | |||
95c9b5d6a2 | |||
05f10f0115 | |||
86b15d381f | |||
ecaab07bce | |||
4fd4efa22f | |||
527585e7eb | |||
481110fefb | |||
c44f69a01f | |||
adbc2a76c3 | |||
34ed201aff | |||
4d63b81b05 | |||
e1a18cdae1 | |||
2a1d87650b | |||
4a18dfeef3 | |||
ff1aece1ed | |||
05cf5e376a | |||
855a66499f | |||
b9cc581736 | |||
0a8eee8af0 | |||
a40fc7e112 | |||
6bbb5669a6 | |||
c8d5411462 | |||
af4cfc29b1 | |||
9942025a2f | |||
04f7287781 | |||
14ae501433 | |||
46edc56a32 | |||
7907623887 | |||
c542e120ef | |||
7fcff0b6a2 | |||
32671201a4 | |||
4d2268b5f1 | |||
e5fe7c093a | |||
162f3a291c | |||
31740befbf | |||
0c610c8f1c | |||
e9dc22c1f2 | |||
75e6393680 | |||
9ca6857f4d | |||
8c30b87a94 | |||
6ffd6693cb | |||
e11fe929f4 | |||
3dcd5629a7 | |||
4cf4c38da3 | |||
e0e3c36d1b | |||
108c1d9d60 | |||
c6e16ebc13 | |||
aa60838551 | |||
d6bde02dfe | |||
d07bb03936 | |||
1ab2f42ff4 | |||
e0d20cb62a | |||
f8944c8379 | |||
ca38bb4aec | |||
287817056f | |||
5cc7ced859 | |||
4dc5378b3e | |||
fe7e440997 | |||
e4262cb0bc | |||
35c9f2bf60 | |||
13794e9eaa | |||
a33950da62 | |||
37995e23c2 | |||
66156829d9 | |||
3c40fa6982 | |||
c1ddddddc0 | |||
aae118b476 | |||
7e402ce974 | |||
5b80308074 | |||
e5c94b410f | |||
209c18cb38 | |||
616a2dd19f | |||
5b0f898c62 | |||
a541e866a1 | |||
d3eb0bee26 | |||
2ca0f6ea62 | |||
66be38bfbf |
11
TODO.md
11
TODO.md
@@ -1,3 +1,7 @@
|
||||
## BUGS
|
||||
- why i need to manually restart `wireguard-wg-ovpns` on servo periodically
|
||||
- else DNS fails
|
||||
|
||||
## REFACTORING:
|
||||
### sops/secrets
|
||||
- attach secrets to the thing they're used by (sane.programs)
|
||||
@@ -9,10 +13,16 @@
|
||||
- will make it easier to test new services?
|
||||
|
||||
### upstreaming
|
||||
- split out a trust-dns module
|
||||
- see: <https://github.com/NixOS/nixpkgs/pull/205866#issuecomment-1575753054>
|
||||
- bump nodejs version in lemmy-ui
|
||||
- add updateScripts to all my packages in nixpkgs
|
||||
- fix lightdm-mobile-greeter for newer libhandy
|
||||
- port zecwallet-lite to a from-source build
|
||||
- fix or abandon Whalebird
|
||||
- FIX failed CI on bonsai PR: <https://github.com/NixOS/nixpkgs/pull/233892>
|
||||
- REVIEW/integrate jellyfin dataDir config: <https://github.com/NixOS/nixpkgs/pull/233617>
|
||||
- remove `libsForQt5.callPackage` broadly: <https://github.com/NixOS/nixpkgs/issues/180841>
|
||||
|
||||
|
||||
## IMPROVEMENTS:
|
||||
@@ -46,6 +56,7 @@
|
||||
- see: <repo:mil/sxmo-utils:scripts/core/sxmo_autorotate.sh>
|
||||
- all orientations *except* upside down are supported
|
||||
- sxmo: launch with auto-rotation enabled
|
||||
- sane-bt-search: show details like 5.1 vs stereo, h264 vs h265
|
||||
|
||||
### perf
|
||||
- why does nixos-rebuild switch take 5 minutes when net is flakey?
|
||||
|
18
flake.lock
generated
18
flake.lock
generated
@@ -66,11 +66,11 @@
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1684025543,
|
||||
"narHash": "sha256-hGe7S+i5je+8E/b2mOXVI9nmr038Dw+bV8e1P8xHSe0=",
|
||||
"lastModified": 1685758009,
|
||||
"narHash": "sha256-IT4Z5WGhafrq+xbDTyuKrRPRQ1f+kVOtE+4JU1CHFeo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c6d2f3dc0d3efd4285eebe4f8a36a47ba438138e",
|
||||
"rev": "eaf03591711b46d21abc7082a8ebee4681f9dbeb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -82,11 +82,11 @@
|
||||
},
|
||||
"nixpkgs-unpatched": {
|
||||
"locked": {
|
||||
"lastModified": 1684385584,
|
||||
"narHash": "sha256-O7y0gK8OLIDqz+LaHJJyeu09IGiXlZIS3+JgEzGmmJA=",
|
||||
"lastModified": 1686135559,
|
||||
"narHash": "sha256-pY8waAV8K/sbHBdLn5diPFnQKpNg0YS9w03MrD2lUGE=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "48a0fb7aab511df92a17cf239c37f2bd2ec9ae3a",
|
||||
"rev": "381e92a35e2d196fdd6077680dca0cd0197e75cb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -113,11 +113,11 @@
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1684032930,
|
||||
"narHash": "sha256-ueeSYDii2e5bkKrsSdP12JhkW9sqgYrUghLC8aDfYGQ=",
|
||||
"lastModified": 1685848844,
|
||||
"narHash": "sha256-Iury+/SVbAwLES76QJSiKFiQDzmf/8Hsq8j54WF2qyw=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "a376127bb5277cd2c337a9458744f370aaf2e08d",
|
||||
"rev": "a522e12ee35e50fa7d902a164a9796e420e6e75b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@@ -19,6 +19,7 @@
|
||||
sane.programs.iphoneUtils.enableFor.user.colin = true;
|
||||
|
||||
sane.programs.guiApps.suggestedPrograms = [ "desktopGuiApps" ];
|
||||
sane.programs.consoleUtils.suggestedPrograms = [ "consoleMediaUtils" ];
|
||||
|
||||
boot.loader.efi.canTouchEfiVariables = false;
|
||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
|
@@ -19,6 +19,7 @@
|
||||
"desktopGuiApps"
|
||||
"stepmania"
|
||||
];
|
||||
sane.programs.consoleUtils.suggestedPrograms = [ "consoleMediaUtils" ];
|
||||
|
||||
sops.secrets.colin-passwd.neededForUsers = true;
|
||||
|
||||
|
@@ -33,11 +33,15 @@
|
||||
".config/pulse" # persist pulseaudio volume
|
||||
];
|
||||
|
||||
sane.gui.phosh.enable = true;
|
||||
sane.gui.sxmo.enable = true;
|
||||
# sane.programs.consoleUtils.enableFor.user.colin = false;
|
||||
# sane.programs.guiApps.enableFor.user.colin = false;
|
||||
sane.programs.sequoia.enableFor.user.colin = false;
|
||||
sane.programs.tuiApps.enableFor.user.colin = false; # visidata, others, don't compile well
|
||||
# disabled for faster deploys (gthumb depends on webkitgtk, particularly)
|
||||
sane.programs.soundconverter.enableFor.user.colin = false;
|
||||
sane.programs."gnome.nautilus".enableFor.user.colin = false;
|
||||
sane.programs.gthumb.enableFor.user.colin = false;
|
||||
|
||||
boot.loader.efi.canTouchEfiVariables = false;
|
||||
# /boot space is at a premium. default was 20.
|
||||
@@ -77,14 +81,30 @@
|
||||
# enable rotation sensor
|
||||
hardware.sensor.iio.enable = true;
|
||||
|
||||
# from https://gitlab.manjaro.org/manjaro-arm/packages/community/phosh/alsa-ucm-pinephone
|
||||
# mobile-nixos does this same thing, with *slightly different settings*.
|
||||
# i trust manjaro more because the guy maintaining that is actively trying to upstream into alsa-ucm-conf.
|
||||
# an alternative may be to build a custom alsa with the PinePhone config patch applied:
|
||||
# - <https://github.com/alsa-project/alsa-ucm-conf/pull/134>
|
||||
# that would make this be not device-specific
|
||||
environment.variables.ALSA_CONFIG_UCM2 = "${./ucm2}";
|
||||
systemd.services.pulseaudio.environment.ALSA_CONFIG_UCM2 = "${./ucm2}";
|
||||
# inject specialized alsa configs via the environment.
|
||||
# specifically, this gets the pinephone headphones & internal earpiece working.
|
||||
# see pkgs/patched/alsa-ucm-conf for more info.
|
||||
environment.variables.ALSA_CONFIG_UCM2 = "/run/current-system/sw/share/alsa/ucm2";
|
||||
environment.pathsToLink = [ "/share/alsa/ucm2" ];
|
||||
environment.systemPackages = [ pkgs.alsa-ucm-conf-sane ];
|
||||
systemd =
|
||||
let ucm-env = config.environment.variables.ALSA_CONFIG_UCM2;
|
||||
in {
|
||||
# cribbed from <repo:nixos/mobile-nixos:modules/quirks/audio.nix>
|
||||
|
||||
# pulseaudio
|
||||
user.services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
|
||||
# pipewire
|
||||
user.services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
user.services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
user.services.wireplumber.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
services.wireplumber.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
};
|
||||
|
||||
|
||||
hardware.opengl.driSupport = true;
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@
|
||||
sane.zsh.showDeadlines = false; # ~/knowledge doesn't always exist
|
||||
sane.services.dyn-dns.enable = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.enableWan = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."servo".wg-home.ip;
|
||||
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade
|
||||
|
||||
|
@@ -3,6 +3,12 @@
|
||||
{
|
||||
networking.domain = "uninsane.org";
|
||||
|
||||
sane.ports.openFirewall = true;
|
||||
sane.ports.openUpnp = true;
|
||||
|
||||
# view refused packets with: `sudo journalctl -k`
|
||||
# networking.firewall.logRefusedPackets = true;
|
||||
|
||||
# The global useDHCP flag is deprecated, therefore explicitly set to false here.
|
||||
# Per-interface useDHCP will be mandatory in the future, so this generated config
|
||||
# replicates the default behaviour.
|
||||
@@ -11,9 +17,6 @@
|
||||
# XXX colin: probably don't need this. wlan0 won't be populated unless i touch a value in networking.interfaces.wlan0
|
||||
networking.wireless.enable = false;
|
||||
|
||||
# networking.firewall.enable = false;
|
||||
networking.firewall.enable = true;
|
||||
|
||||
# this is needed to forward packets from the VPN to the host
|
||||
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
|
||||
|
||||
@@ -153,9 +156,9 @@
|
||||
|
||||
# we also bridge DNS traffic
|
||||
${in-ns} ${iptables} -A PREROUTING -t nat -p udp --dport 53 -m iprange --dst-range ${vpn-ip} \
|
||||
-j DNAT --to-destination ${veth-host-ip}:53
|
||||
-j DNAT --to-destination ${veth-host-ip}
|
||||
${in-ns} ${iptables} -A PREROUTING -t nat -p tcp --dport 53 -m iprange --dst-range ${vpn-ip} \
|
||||
-j DNAT --to-destination ${veth-host-ip}:53
|
||||
-j DNAT --to-destination ${veth-host-ip}
|
||||
|
||||
# in order to access DNS in this netns, we need to route it to the VPN's nameservers
|
||||
# - alternatively, we could fix DNS servers like 1.1.1.1.
|
||||
|
@@ -30,5 +30,5 @@ lib.mkIf false
|
||||
proxyPass = "http://${ip}:${builtins.toString port}";
|
||||
};
|
||||
};
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."calibre" = "native";
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."calibre" = "native";
|
||||
}
|
||||
|
@@ -22,20 +22,60 @@
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "ejabberd"; group = "ejabberd"; directory = "/var/lib/ejabberd"; }
|
||||
];
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
3478 # STUN/TURN
|
||||
5222 # XMPP client -> server
|
||||
5223 # XMPPS client -> server (XMPP over TLS)
|
||||
5269 # XMPP server -> server
|
||||
5270 # XMPPS server -> server (XMPP over TLS)
|
||||
5280 # bosh
|
||||
5281 # bosh (https) ??
|
||||
5349 # STUN/TURN (TLS)
|
||||
5443 # web services (file uploads, websockets, admin)
|
||||
];
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
3478 # STUN/TURN
|
||||
];
|
||||
sane.ports.ports."3478" = {
|
||||
protocol = [ "tcp" "udp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-stun-turn";
|
||||
};
|
||||
sane.ports.ports."5222" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-client-to-server";
|
||||
};
|
||||
sane.ports.ports."5223" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpps-client-to-server"; # XMPP over TLS
|
||||
};
|
||||
sane.ports.ports."5269" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-server-to-server";
|
||||
};
|
||||
sane.ports.ports."5270" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpps-server-to-server"; # XMPP over TLS
|
||||
};
|
||||
sane.ports.ports."5280" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-bosh";
|
||||
};
|
||||
sane.ports.ports."5281" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-bosh-https";
|
||||
};
|
||||
sane.ports.ports."5349" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-stun-turn-over-tls";
|
||||
};
|
||||
sane.ports.ports."5443" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-web-services"; # file uploads, websockets, admin
|
||||
};
|
||||
|
||||
# TODO: forward these TURN ports!
|
||||
networking.firewall.allowedTCPPortRanges = [{
|
||||
from = 49152; # TURN
|
||||
to = 49408;
|
||||
@@ -75,9 +115,9 @@
|
||||
useACMEHost = "uninsane.org";
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet = {
|
||||
sane.dns.zones."uninsane.org".inet = {
|
||||
# XXX: SRV records have to point to something with a A/AAAA record; no CNAMEs
|
||||
A."xmpp" = "%NATIVE%";
|
||||
A."xmpp" = "%ANATIVE%";
|
||||
CNAME."muc.xmpp" = "xmpp";
|
||||
CNAME."pubsub.xmpp" = "xmpp";
|
||||
CNAME."upload.xmpp" = "xmpp";
|
||||
@@ -234,7 +274,7 @@
|
||||
use_turn: true
|
||||
turn_min_port: 49152
|
||||
turn_max_port: 65535
|
||||
turn_ipv4_address: %NATIVE%
|
||||
turn_ipv4_address: %ANATIVE%
|
||||
-
|
||||
# STUN+TURN UDP
|
||||
port: 3478
|
||||
@@ -243,7 +283,7 @@
|
||||
use_turn: true
|
||||
turn_min_port: 49152
|
||||
turn_max_port: 65535
|
||||
turn_ipv4_address: %NATIVE%
|
||||
turn_ipv4_address: %ANATIVE%
|
||||
-
|
||||
# STUN+TURN TLS over TCP
|
||||
port: 5349
|
||||
@@ -254,7 +294,7 @@
|
||||
use_turn: true
|
||||
turn_min_port: 49152
|
||||
turn_max_port: 65535
|
||||
turn_ipv4_address: %NATIVE%
|
||||
turn_ipv4_address: %ANATIVE%
|
||||
|
||||
# TODO: enable mod_fail2ban
|
||||
# TODO(low): look into mod_http_fileserver for serving macros?
|
||||
@@ -387,7 +427,7 @@
|
||||
# config is 444 (not 644), so we want to write out-of-place and then atomically move
|
||||
# TODO: factor this out into `sane-woop` helper?
|
||||
rm -f /var/lib/ejabberd/ejabberd.yaml.new
|
||||
${sed} "s/%NATIVE%/$ip/" ${config-in} > /var/lib/ejabberd/ejabberd.yaml.new
|
||||
${sed} "s/%ANATIVE%/$ip/" ${config-in} > /var/lib/ejabberd/ejabberd.yaml.new
|
||||
mv /var/lib/ejabberd/ejabberd.yaml{.new,}
|
||||
'';
|
||||
|
||||
|
@@ -6,18 +6,25 @@
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
# exposed over non-vpn imap.uninsane.org
|
||||
143 # IMAP
|
||||
993 # IMAPS
|
||||
];
|
||||
sane.ports.ports."143" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-imap-imap.uninsane.org";
|
||||
};
|
||||
sane.ports.ports."993" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-imaps-imap.uninsane.org";
|
||||
};
|
||||
|
||||
# exists only to manage certs for dovecot
|
||||
services.nginx.virtualHosts."imap.uninsane.org" = {
|
||||
enableACME = true;
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet = {
|
||||
sane.dns.zones."uninsane.org".inet = {
|
||||
CNAME."imap" = "native";
|
||||
};
|
||||
|
||||
|
@@ -28,12 +28,21 @@ in
|
||||
# "/var/lib/dovecot"
|
||||
];
|
||||
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
# exposed over vpn mx.uninsane.org
|
||||
25 # SMTP
|
||||
465 # SMTPS
|
||||
587 # SMTPS/submission
|
||||
];
|
||||
sane.ports.ports."25" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.ovpn = true;
|
||||
description = "colin-smtp-mx.uninsane.org";
|
||||
};
|
||||
sane.ports.ports."465" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.ovpn = true;
|
||||
description = "colin-smtps-mx.uninsane.org";
|
||||
};
|
||||
sane.ports.ports."587" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.ovpn = true;
|
||||
description = "colin-smtps-submission-mx.uninsane.org";
|
||||
};
|
||||
|
||||
# exists only to manage certs for Postfix
|
||||
services.nginx.virtualHosts."mx.uninsane.org" = {
|
||||
@@ -41,7 +50,7 @@ in
|
||||
};
|
||||
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet = {
|
||||
sane.dns.zones."uninsane.org".inet = {
|
||||
MX."@" = "10 mx.uninsane.org.";
|
||||
# XXX: RFC's specify that the MX record CANNOT BE A CNAME
|
||||
A."mx" = "185.157.162.178";
|
||||
|
@@ -59,5 +59,5 @@
|
||||
# the routing is handled by services.freshrss.virtualHost
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."rss" = "native";
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."rss" = "native";
|
||||
}
|
||||
|
@@ -98,5 +98,12 @@
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."git" = "native";
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."git" = "native";
|
||||
|
||||
sane.ports.ports."22" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-git@git.uninsane.org";
|
||||
};
|
||||
}
|
||||
|
@@ -64,5 +64,5 @@
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."sink" = "native";
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."sink" = "native";
|
||||
}
|
||||
|
@@ -34,7 +34,7 @@ lib.mkIf false # i don't actively use ipfs anymore
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."ipfs" = "native";
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."ipfs" = "native";
|
||||
|
||||
# services.ipfs.enable = true;
|
||||
services.kubo.localDiscovery = true;
|
||||
|
@@ -27,6 +27,6 @@
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."jackett" = "native";
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."jackett" = "native";
|
||||
}
|
||||
|
||||
|
@@ -16,17 +16,30 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
# identical to:
|
||||
# services.jellyfin.openFirewall = true;
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
# https://jellyfin.org/docs/general/networking/index.html
|
||||
1900 # UPnP service discovery
|
||||
7359 # Jellyfin-specific (?) client discovery
|
||||
];
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
8096 # HTTP (for the LAN)
|
||||
8920 # HTTPS (for the LAN)
|
||||
];
|
||||
# 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.plaintext = [
|
||||
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; directory = "/var/lib/jellyfin"; }
|
||||
];
|
||||
@@ -108,7 +121,7 @@
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."jelly" = "native";
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."jelly" = "native";
|
||||
|
||||
services.jellyfin.enable = true;
|
||||
}
|
||||
|
@@ -13,5 +13,5 @@
|
||||
locations."/".proxyPass = "http://127.0.0.1:8013";
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."w" = "native";
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."w" = "native";
|
||||
}
|
||||
|
@@ -18,5 +18,5 @@ in
|
||||
proxyPass = "http://127.0.0.1:${builtins.toString port}";
|
||||
};
|
||||
};
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."komga" = "native";
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."komga" = "native";
|
||||
}
|
||||
|
@@ -54,5 +54,5 @@ in {
|
||||
enableACME = true;
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."lemmy" = "native";
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."lemmy" = "native";
|
||||
}
|
||||
|
@@ -132,7 +132,7 @@
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet = {
|
||||
sane.dns.zones."uninsane.org".inet = {
|
||||
CNAME."matrix" = "native";
|
||||
CNAME."web.matrix" = "native";
|
||||
};
|
||||
|
@@ -1,4 +1,9 @@
|
||||
{ lib, ... }:
|
||||
|
||||
# XXX mx-discord-puppet uses nodejs_14 which is EOL
|
||||
# - mx-discord-puppet is abandoned upstream _and_ in nixpkgs
|
||||
# - recommended to use mautrix-discord: <https://github.com/NixOS/nixpkgs/pull/200462>
|
||||
lib.mkIf false
|
||||
{
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/mx-puppet-discord"; }
|
||||
|
@@ -36,5 +36,5 @@
|
||||
locations."/".proxyPass = "http://127.0.0.1:4533";
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."music" = "native";
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."music" = "native";
|
||||
}
|
||||
|
@@ -13,7 +13,19 @@ let
|
||||
in
|
||||
{
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||
sane.ports.ports."80" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
visibleTo.ovpn = true; # so that letsencrypt can procure a cert for the mx record
|
||||
description = "colin-http-uninsane.org";
|
||||
};
|
||||
sane.ports.ports."443" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-https-uninsane.org";
|
||||
};
|
||||
|
||||
services.nginx.enable = true;
|
||||
services.nginx.appendConfig = ''
|
||||
|
@@ -14,7 +14,7 @@
|
||||
'';
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."nixcache" = "native";
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."nixcache" = "native";
|
||||
|
||||
sane.services.nixserve.enable = true;
|
||||
sane.services.nixserve.secretKeyFile = config.sops.secrets.nix_serve_privkey.path;
|
||||
|
@@ -182,7 +182,7 @@
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."fed" = "native";
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."fed" = "native";
|
||||
|
||||
sops.secrets."pleroma_secrets" = {
|
||||
owner = config.users.users.pleroma.name;
|
||||
|
@@ -12,12 +12,29 @@ lib.mkIf false
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "prosody"; group = "prosody"; directory = "/var/lib/prosody"; }
|
||||
];
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
5222 # XMPP client -> server
|
||||
5269 # XMPP server -> server
|
||||
5280 # bosh
|
||||
5281 # Prosody HTTPS port (necessary?)
|
||||
];
|
||||
sane.ports.ports."5222" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-client-to-server";
|
||||
};
|
||||
sane.ports.ports."5269" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-server-to-server";
|
||||
};
|
||||
sane.ports.ports."5280" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-bosh";
|
||||
};
|
||||
sane.ports.ports."5281" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-prosody-https"; # necessary?
|
||||
};
|
||||
|
||||
# provide access to certs
|
||||
users.users.prosody.extraGroups = [ "nginx" ];
|
||||
|
@@ -75,6 +75,6 @@
|
||||
};
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."bt" = "native";
|
||||
sane.dns.zones."uninsane.org".inet.CNAME."bt" = "native";
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
sane.services.trust-dns.enable = true;
|
||||
@@ -11,7 +11,7 @@
|
||||
];
|
||||
sane.services.trust-dns.quiet = true;
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".TTL = 900;
|
||||
sane.dns.zones."uninsane.org".TTL = 900;
|
||||
|
||||
# SOA record structure: <https://en.wikipedia.org/wiki/SOA_record#Structure>
|
||||
# SOA MNAME RNAME (... rest)
|
||||
@@ -21,7 +21,7 @@
|
||||
# Refresh = how frequently secondary NS should query master
|
||||
# Retry = how long secondary NS should wait until re-querying master after a failure (must be < Refresh)
|
||||
# Expire = how long secondary NS should continue to reply to queries after master fails (> Refresh + Retry)
|
||||
sane.services.trust-dns.zones."uninsane.org".inet = {
|
||||
sane.dns.zones."uninsane.org".inet = {
|
||||
SOA."@" = ''
|
||||
ns1.uninsane.org. admin-dns.uninsane.org. (
|
||||
2022122101 ; Serial
|
||||
@@ -30,17 +30,20 @@
|
||||
7d ; Expire
|
||||
5m) ; Negative response TTL
|
||||
'';
|
||||
TXT."rev" = "2022122101";
|
||||
TXT."rev" = "2023052901";
|
||||
|
||||
CNAME."native" = "%CNAMENATIVE%";
|
||||
A."@" = "%ANATIVE%";
|
||||
A."wan" = "%AWAN%";
|
||||
A."servo.lan" = config.sane.hosts.by-name."servo".lan-ip;
|
||||
|
||||
# XXX NS records must also not be CNAME
|
||||
# it's best that we keep this identical, or a superset of, what org. lists as our NS.
|
||||
# so, org. can specify ns2/ns3 as being to the VPN, with no mention of ns1. we provide ns1 here.
|
||||
A."ns1" = "%NATIVE%";
|
||||
A."ns1" = "%ANATIVE%";
|
||||
A."ns2" = "185.157.162.178";
|
||||
A."ns3" = "185.157.162.178";
|
||||
A."ovpns" = "185.157.162.178";
|
||||
A."native" = "%NATIVE%";
|
||||
A."@" = "%NATIVE%";
|
||||
NS."@" = [
|
||||
"ns1.uninsane.org."
|
||||
"ns2.uninsane.org."
|
||||
@@ -48,20 +51,70 @@
|
||||
];
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".file =
|
||||
"/var/lib/trust-dns/uninsane.org.zone";
|
||||
# we need trust-dns to load our zone by relative path instead of /nix/store path
|
||||
# because we generate it at runtime.
|
||||
sane.services.trust-dns.zones."uninsane.org".file = lib.mkForce "uninsane.org.zone";
|
||||
sane.services.trust-dns.zonedir = null;
|
||||
|
||||
systemd.services.trust-dns.preStart = let
|
||||
sed = "${pkgs.gnused}/bin/sed";
|
||||
zone-dir = "/var/lib/trust-dns";
|
||||
zone-out = "${zone-dir}/uninsane.org.zone";
|
||||
zone-template = pkgs.writeText "uninsane.org.zone.in" config.sane.services.trust-dns.generatedZones."uninsane.org";
|
||||
in ''
|
||||
# make WAN records available to trust-dns
|
||||
mkdir -p ${zone-dir}
|
||||
ip=$(cat '${config.sane.services.dyn-dns.ipPath}')
|
||||
${sed} s/%NATIVE%/$ip/ ${zone-template} > ${zone-out}
|
||||
'';
|
||||
sane.services.trust-dns.package =
|
||||
let
|
||||
sed = "${pkgs.gnused}/bin/sed";
|
||||
zone-dir = "/var/lib/trust-dns";
|
||||
zone-wan = "${zone-dir}/wan/uninsane.org.zone";
|
||||
zone-lan = "${zone-dir}/lan/uninsane.org.zone";
|
||||
zone-template = pkgs.writeText "uninsane.org.zone.in" config.sane.services.trust-dns.zones."uninsane.org".text;
|
||||
in pkgs.writeShellScriptBin "named" ''
|
||||
# compute wan/lan values
|
||||
mkdir -p ${zone-dir}/{ovpn,wan,lan}
|
||||
wan=$(cat '${config.sane.services.dyn-dns.ipPath}')
|
||||
lan=${config.sane.hosts.by-name."servo".lan-ip}
|
||||
|
||||
# create specializations that resolve native.uninsane.org to different CNAMEs
|
||||
${sed} s/%AWAN%/$wan/ ${zone-template} \
|
||||
| ${sed} s/%CNAMENATIVE%/wan/ \
|
||||
| ${sed} s/%ANATIVE%/$wan/ \
|
||||
> ${zone-wan}
|
||||
${sed} s/%AWAN%/$wan/ ${zone-template} \
|
||||
| ${sed} s/%CNAMENATIVE%/servo.lan/ \
|
||||
| ${sed} s/%ANATIVE%/$lan/ \
|
||||
> ${zone-lan}
|
||||
|
||||
# launch the different interfaces, separately
|
||||
${pkgs.trust-dns}/bin/named --port 53 --zonedir ${zone-dir}/wan/ $@ &
|
||||
WANPID=$!
|
||||
${pkgs.trust-dns}/bin/named --port 1053 --zonedir ${zone-dir}/lan/ $@ &
|
||||
LANPID=$!
|
||||
|
||||
# wait until any of the processes exits, then kill them all and exit error
|
||||
while kill -0 $WANPID $LANPID ; do
|
||||
sleep 5
|
||||
done
|
||||
kill $WANPID $LANPID
|
||||
exit 1
|
||||
'';
|
||||
|
||||
sane.services.dyn-dns.restartOnChange = [ "trust-dns.service" ];
|
||||
|
||||
networking.nat.enable = true;
|
||||
networking.nat.extraCommands = ''
|
||||
# redirect incoming DNS requests from LAN addresses
|
||||
# to the LAN-specialized DNS service
|
||||
# N.B.: use the `nixos-*` chains instead of e.g. PREROUTING
|
||||
# because they get cleanly reset across activations or `systemctl restart firewall`
|
||||
# instead of accumulating cruft
|
||||
iptables -t nat -A nixos-nat-pre -p udp --dport 53 \
|
||||
-m iprange --src-range 10.78.76.0-10.78.79.255 \
|
||||
-j DNAT --to-destination :1053
|
||||
iptables -t nat -A nixos-nat-pre -p tcp --dport 53 \
|
||||
-m iprange --src-range 10.78.76.0-10.78.79.255 \
|
||||
-j DNAT --to-destination :1053
|
||||
'';
|
||||
|
||||
sane.ports.ports."1053" = {
|
||||
# because the NAT above redirects in nixos-nat-pre, LAN requests behave as though they arrived on the external interface at the redirected port.
|
||||
# TODO: try nixos-nat-post instead?
|
||||
protocol = [ "udp" "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
description = "colin-redirected-dns-for-lan-namespace";
|
||||
};
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@
|
||||
./ids.nix
|
||||
./machine-id.nix
|
||||
./net.nix
|
||||
./nix-path
|
||||
./persist.nix
|
||||
./programs
|
||||
./secrets.nix
|
||||
@@ -36,11 +37,6 @@
|
||||
nix.extraOptions = ''
|
||||
experimental-features = nix-command flakes
|
||||
'';
|
||||
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages
|
||||
nix.nixPath = [
|
||||
"nixpkgs=${pkgs.path}"
|
||||
"nixpkgs-overlays=${../..}/overlays"
|
||||
];
|
||||
# hardlinks identical files in the nix store to save 25-35% disk space.
|
||||
# unclear _when_ this occurs. it's not a service.
|
||||
# does the daemon continually scan the nix store?
|
||||
|
@@ -76,7 +76,7 @@ let
|
||||
## Multidisciplinary Association for Psychedelic Studies
|
||||
(fromDb "mapspodcast.libsyn.com" // uncat)
|
||||
(fromDb "allinchamathjason.libsyn.com" // pol)
|
||||
(fromDb "acquired.libsyn.com" // tech)
|
||||
(fromDb "feeds.transistor.fm/acquired" // tech)
|
||||
## ACQ2 - more "Acquired" episodes
|
||||
(fromDb "acquiredlpbonussecretsecret.libsyn.com" // tech)
|
||||
# The Intercept - Deconstructed; also available: <rss.acast.com/deconstructed>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
# the default backend is "wpa_supplicant".
|
||||
@@ -20,4 +20,8 @@
|
||||
General.RoamThreshold = "-52"; # default -70
|
||||
General.RoamThreshold5G = "-52"; # default -76
|
||||
};
|
||||
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
1900 # to received UPnP advertisements. required by sane-ip-check-upnp
|
||||
];
|
||||
}
|
||||
|
10
hosts/common/nix-path/default.nix
Normal file
10
hosts/common/nix-path/default.nix
Normal file
@@ -0,0 +1,10 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages
|
||||
nix.nixPath = [
|
||||
"nixpkgs=${pkgs.path}"
|
||||
# note the import starts at repo root: this allows `./overlay/default.nix` to access the stuff at the root
|
||||
"nixpkgs-overlays=${../../..}/hosts/common/nix-path/overlay"
|
||||
];
|
||||
}
|
4
hosts/common/nix-path/overlay/default.nix
Normal file
4
hosts/common/nix-path/overlay/default.nix
Normal file
@@ -0,0 +1,4 @@
|
||||
# XXX: NIX_PATH=...:nixpkgs-overlays=... will import every overlay in the directory
|
||||
# so we prefer to give it a directory with just this *one* overlay, otherwise it imports conflicting overlays
|
||||
# and gets stuck in a loop until it OOMs
|
||||
import ../../../../overlays/all.nix
|
381
hosts/common/programs/assorted.nix
Normal file
381
hosts/common/programs/assorted.nix
Normal file
@@ -0,0 +1,381 @@
|
||||
{ lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
inherit (builtins) attrNames;
|
||||
|
||||
flattenedPkgs = pkgs // (with pkgs; {
|
||||
# XXX can't `inherit` a nested attr, so we move them to the toplevel
|
||||
"cacert.unbundled" = pkgs.cacert.unbundled;
|
||||
"gnome.cheese" = gnome.cheese;
|
||||
"gnome.dconf-editor" = gnome.dconf-editor;
|
||||
"gnome.file-roller" = gnome.file-roller;
|
||||
"gnome.gnome-disk-utility" = gnome.gnome-disk-utility;
|
||||
"gnome.gnome-maps" = gnome.gnome-maps;
|
||||
"gnome.nautilus" = gnome.nautilus;
|
||||
"gnome.gnome-system-monitor" = gnome.gnome-system-monitor;
|
||||
"gnome.gnome-terminal" = gnome.gnome-terminal;
|
||||
"gnome.gnome-weather" = gnome.gnome-weather;
|
||||
"gnome.totem" = gnome.totem;
|
||||
"libsForQt5.plasmatube" = libsForQt5.plasmatube;
|
||||
});
|
||||
|
||||
sysadminPkgs = {
|
||||
inherit (flattenedPkgs)
|
||||
btrfs-progs
|
||||
"cacert.unbundled" # some services require unbundled /etc/ssl/certs
|
||||
cryptsetup
|
||||
dig
|
||||
efibootmgr
|
||||
fatresize
|
||||
fd
|
||||
file
|
||||
gawk
|
||||
git
|
||||
gptfdisk
|
||||
hdparm
|
||||
htop
|
||||
iftop
|
||||
inetutils # for telnet
|
||||
iotop
|
||||
iptables
|
||||
jq
|
||||
killall
|
||||
lsof
|
||||
miniupnpc
|
||||
nano
|
||||
netcat
|
||||
nethogs
|
||||
nmap
|
||||
openssl
|
||||
parted
|
||||
pciutils
|
||||
powertop
|
||||
pstree
|
||||
ripgrep
|
||||
screen
|
||||
smartmontools
|
||||
socat
|
||||
strace
|
||||
subversion
|
||||
tcpdump
|
||||
tree
|
||||
usbutils
|
||||
wget
|
||||
wirelesstools # iwlist
|
||||
;
|
||||
};
|
||||
sysadminExtraPkgs = {
|
||||
# application-specific packages
|
||||
inherit (pkgs)
|
||||
backblaze-b2
|
||||
duplicity
|
||||
sqlite # to debug sqlite3 databases
|
||||
;
|
||||
};
|
||||
|
||||
iphonePkgs = {
|
||||
inherit (pkgs)
|
||||
ifuse
|
||||
ipfs
|
||||
libimobiledevice
|
||||
;
|
||||
};
|
||||
|
||||
tuiPkgs = {
|
||||
inherit (pkgs)
|
||||
aerc # email client
|
||||
offlineimap # email mailox sync
|
||||
visidata # TUI spreadsheet viewer/editor
|
||||
w3m
|
||||
;
|
||||
};
|
||||
|
||||
consoleMediaPkgs = {
|
||||
inherit (pkgs)
|
||||
ffmpeg
|
||||
imagemagick
|
||||
sox
|
||||
yt-dlp
|
||||
;
|
||||
};
|
||||
# TODO: split these into smaller groups.
|
||||
# - moby doesn't want a lot of these.
|
||||
# - categories like
|
||||
# - dev?
|
||||
# - debugging?
|
||||
consolePkgs = {
|
||||
inherit (pkgs)
|
||||
alsaUtils # for aplay, speaker-test
|
||||
cdrtools
|
||||
clinfo
|
||||
dmidecode
|
||||
efivar
|
||||
flashrom
|
||||
fwupd
|
||||
gh # MS GitHub cli
|
||||
git # needed as a user package, for config.
|
||||
gnupg
|
||||
gocryptfs
|
||||
gopass # TODO: shouldn't be needed here
|
||||
gopass-jsonapi
|
||||
kitty # TODO: move to GUI, but `ssh servo` from kitty sets `TERM=xterm-kitty` in the remove and breaks things
|
||||
libsecret # for managing user keyrings
|
||||
lm_sensors # for sensors-detect
|
||||
lshw
|
||||
# memtester
|
||||
neovim
|
||||
# nettools
|
||||
# networkmanager
|
||||
nixpkgs-review
|
||||
# nixos-generators
|
||||
nmon
|
||||
# node2nix
|
||||
# oathToolkit # for oathtool
|
||||
# ponymix
|
||||
pulsemixer
|
||||
python3
|
||||
ripgrep # needed as a user package so that its user-level config file can be installed
|
||||
rsync
|
||||
# python3Packages.eyeD3 # music tagging
|
||||
sane-scripts
|
||||
sequoia
|
||||
snapper
|
||||
sops
|
||||
speedtest-cli
|
||||
# ssh-to-age
|
||||
sudo
|
||||
# tageditor # music tagging
|
||||
unar
|
||||
wireguard-tools
|
||||
xdg-utils # for xdg-open
|
||||
# yarn
|
||||
zsh
|
||||
;
|
||||
};
|
||||
|
||||
guiPkgs = {
|
||||
inherit (flattenedPkgs)
|
||||
# celluloid # mpv frontend
|
||||
# emote
|
||||
evince # works on phosh
|
||||
|
||||
# { pkg = fluffychat-moby; persist.plaintext = [ ".local/share/chat.fluffy.fluffychat" ]; } # TODO: ship normal fluffychat on non-moby?
|
||||
|
||||
# foliate # e-book reader
|
||||
|
||||
# XXX by default fractal stores its state in ~/.local/share/<UUID>.
|
||||
# after logging in, manually change ~/.local/share/keyrings/... to point it to some predictable subdir.
|
||||
# then reboot (so that libsecret daemon re-loads the keyring...?)
|
||||
# { pkg = fractal-latest; persist.private = [ ".local/share/fractal" ]; }
|
||||
# { pkg = fractal-next; persist.private = [ ".local/share/fractal" ]; }
|
||||
|
||||
# "gnome.cheese"
|
||||
# gnome-feeds # RSS reader (with claimed mobile support)
|
||||
"gnome.file-roller"
|
||||
# "gnome.gnome-maps" # works on phosh
|
||||
"gnome.nautilus"
|
||||
# gnome-podcasts
|
||||
# "gnome.gnome-system-monitor"
|
||||
# "gnome.gnome-terminal" # works on phosh
|
||||
# "gnome.gnome-weather"
|
||||
gpodder
|
||||
gthumb
|
||||
jellyfin-media-player
|
||||
# lollypop
|
||||
# mpv
|
||||
# networkmanagerapplet
|
||||
# newsflash
|
||||
nheko
|
||||
pavucontrol
|
||||
# picard # music tagging
|
||||
# "libsForQt5.plasmatube" # Youtube player
|
||||
soundconverter
|
||||
# sublime-music
|
||||
# tdesktop # broken on phosh
|
||||
# tokodon
|
||||
vlc
|
||||
# pleroma client (Electron). input is broken on phosh. TODO(2023/02/02): fix electron19 input (insecure)
|
||||
# whalebird
|
||||
xterm # broken on phosh
|
||||
;
|
||||
};
|
||||
desktopGuiPkgs = {
|
||||
inherit (flattenedPkgs)
|
||||
audacity
|
||||
brave # for the integrated wallet -- as a backup
|
||||
chromium
|
||||
dino
|
||||
electrum
|
||||
element-desktop
|
||||
font-manager
|
||||
gajim # XMPP client
|
||||
gimp # broken on phosh
|
||||
"gnome.dconf-editor"
|
||||
"gnome.gnome-disk-utility"
|
||||
# "gnome.totem" # video player, supposedly supports UPnP
|
||||
handbrake
|
||||
hase
|
||||
inkscape
|
||||
kdenlive
|
||||
kid3 # audio tagging
|
||||
krita
|
||||
libreoffice-fresh
|
||||
mumble
|
||||
obsidian
|
||||
slic3r
|
||||
steam
|
||||
wireshark # could maybe ship the cli as sysadmin pkg
|
||||
;
|
||||
};
|
||||
x86GuiPkgs = {
|
||||
inherit (pkgs)
|
||||
discord
|
||||
|
||||
# kaiteki # Pleroma client
|
||||
# gnome.zenity # for kaiteki (it will use qarma, kdialog, or zenity)
|
||||
# gpt2tc # XXX: unreliable mirror
|
||||
|
||||
# logseq # Personal Knowledge Management
|
||||
losslesscut-bin
|
||||
makemkv
|
||||
monero-gui
|
||||
signal-desktop
|
||||
spotify
|
||||
tor-browser-bundle-bin
|
||||
zecwallet-lite
|
||||
;
|
||||
};
|
||||
|
||||
# packages not part of any package set; not enabled by default
|
||||
otherPkgs = {
|
||||
inherit (pkgs)
|
||||
lemmy-server
|
||||
mx-sanebot
|
||||
stepmania
|
||||
;
|
||||
};
|
||||
|
||||
# define -- but don't enable -- the packages in some attrset.
|
||||
declarePkgs = pkgsAsAttrs: lib.mapAttrs (_n: p: {
|
||||
# no need to actually define the package here: it's defaulted
|
||||
# package = mkDefault p;
|
||||
}) pkgsAsAttrs;
|
||||
in
|
||||
{
|
||||
sane.programs = lib.mkMerge [
|
||||
(declarePkgs consoleMediaPkgs)
|
||||
(declarePkgs consolePkgs)
|
||||
(declarePkgs desktopGuiPkgs)
|
||||
(declarePkgs guiPkgs)
|
||||
(declarePkgs iphonePkgs)
|
||||
(declarePkgs sysadminPkgs)
|
||||
(declarePkgs sysadminExtraPkgs)
|
||||
(declarePkgs tuiPkgs)
|
||||
(declarePkgs x86GuiPkgs)
|
||||
(declarePkgs otherPkgs)
|
||||
{
|
||||
# link the various package sets into their own meta packages
|
||||
consoleMediaUtils = {
|
||||
package = null;
|
||||
suggestedPrograms = attrNames consoleMediaPkgs;
|
||||
};
|
||||
consoleUtils = {
|
||||
package = null;
|
||||
suggestedPrograms = attrNames consolePkgs;
|
||||
};
|
||||
desktopGuiApps = {
|
||||
package = null;
|
||||
suggestedPrograms = attrNames desktopGuiPkgs;
|
||||
};
|
||||
guiApps = {
|
||||
package = null;
|
||||
suggestedPrograms = (attrNames guiPkgs)
|
||||
++ [ "web-browser" ]
|
||||
++ [ "tuiApps" ]
|
||||
++ lib.optional (pkgs.system == "x86_64-linux") "x86GuiApps";
|
||||
};
|
||||
iphoneUtils = {
|
||||
package = null;
|
||||
suggestedPrograms = attrNames iphonePkgs;
|
||||
};
|
||||
sysadminUtils = {
|
||||
package = null;
|
||||
suggestedPrograms = attrNames sysadminPkgs;
|
||||
};
|
||||
sysadminExtraUtils = {
|
||||
package = null;
|
||||
suggestedPrograms = attrNames sysadminExtraPkgs;
|
||||
};
|
||||
tuiApps = {
|
||||
package = null;
|
||||
suggestedPrograms = attrNames tuiPkgs;
|
||||
};
|
||||
x86GuiApps = {
|
||||
package = null;
|
||||
suggestedPrograms = attrNames x86GuiPkgs;
|
||||
};
|
||||
}
|
||||
{
|
||||
# nontrivial package definitions
|
||||
|
||||
dino.persist.private = [ ".local/share/dino" ];
|
||||
|
||||
# creds, but also 200 MB of node modules, etc
|
||||
discord.persist.private = [ ".config/discord" ];
|
||||
|
||||
# creds/session keys, etc
|
||||
element-desktop.persist.private = [ ".config/Element" ];
|
||||
|
||||
# `emote` will show a first-run dialog based on what's in this directory.
|
||||
# mostly, it just keeps a LRU of previously-used emotes to optimize display order.
|
||||
# TODO: package [smile](https://github.com/mijorus/smile) for probably a better mobile experience.
|
||||
emote.persist.plaintext = [ ".local/share/Emote" ];
|
||||
|
||||
# MS GitHub stores auth token in .config
|
||||
# TODO: we can populate gh's stuff statically; it even lets us use the same oauth across machines
|
||||
gh.persist.private = [ ".config/gh" ];
|
||||
|
||||
# actual monero blockchain (not wallet/etc; safe to delete, just slow to regenerate)
|
||||
# XXX: is it really safe to persist this? it doesn't have info that could de-anonymize if captured?
|
||||
monero-gui.persist.plaintext = [ ".bitmonero" ];
|
||||
|
||||
mumble.persist.private = [ ".local/share/Mumble" ];
|
||||
|
||||
# not strictly necessary, but allows caching articles; offline use, etc.
|
||||
nheko.persist.private = [
|
||||
".config/nheko" # config file (including client token)
|
||||
".cache/nheko" # media cache
|
||||
".local/share/nheko" # per-account state database
|
||||
];
|
||||
|
||||
# settings (electron app)
|
||||
obsidian.persist.plaintext = [ ".config/obsidian" ];
|
||||
|
||||
# creds, media
|
||||
signal-desktop.persist.private = [ ".config/Signal" ];
|
||||
|
||||
# printer/filament settings
|
||||
slic3r.persist.plaintext = [ ".Slic3r" ];
|
||||
|
||||
# creds, widevine .so download. TODO: could easily manage these statically.
|
||||
spotify.persist.plaintext = [ ".config/spotify" ];
|
||||
|
||||
tdesktop.persist.private = [ ".local/share/TelegramDesktop" ];
|
||||
|
||||
tokodon.persist.private = [ ".cache/KDE/tokodon" ];
|
||||
|
||||
# hardenedMalloc solves a crash at startup
|
||||
# TODO 2023/02/02: is this safe to remove yet?
|
||||
tor-browser-bundle-bin.package = pkgs.tor-browser-bundle-bin.override {
|
||||
useHardenedMalloc = false;
|
||||
};
|
||||
|
||||
whalebird.persist.private = [ ".config/Whalebird" ];
|
||||
|
||||
yarn.persist.plaintext = [ ".cache/yarn" ];
|
||||
|
||||
# zcash coins. safe to delete, just slow to regenerate (10-60 minutes)
|
||||
zecwallet-lite.persist.private = [ ".zcash" ];
|
||||
}
|
||||
];
|
||||
}
|
@@ -1,266 +1,14 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ pkgs, ... }:
|
||||
|
||||
let
|
||||
inherit (builtins) attrNames concatLists;
|
||||
inherit (lib) mapAttrs mapAttrsToList mkDefault mkIf mkMerge optional;
|
||||
|
||||
flattenedPkgs = pkgs // (with pkgs; {
|
||||
# XXX can't `inherit` a nested attr, so we move them to the toplevel
|
||||
"cacert.unbundled" = pkgs.cacert.unbundled;
|
||||
"gnome.cheese" = gnome.cheese;
|
||||
"gnome.dconf-editor" = gnome.dconf-editor;
|
||||
"gnome.file-roller" = gnome.file-roller;
|
||||
"gnome.gnome-disk-utility" = gnome.gnome-disk-utility;
|
||||
"gnome.gnome-maps" = gnome.gnome-maps;
|
||||
"gnome.nautilus" = gnome.nautilus;
|
||||
"gnome.gnome-system-monitor" = gnome.gnome-system-monitor;
|
||||
"gnome.gnome-terminal" = gnome.gnome-terminal;
|
||||
"gnome.gnome-weather" = gnome.gnome-weather;
|
||||
"gnome.totem" = gnome.totem;
|
||||
"libsForQt5.plasmatube" = libsForQt5.plasmatube;
|
||||
});
|
||||
|
||||
sysadminPkgs = {
|
||||
inherit (flattenedPkgs)
|
||||
btrfs-progs
|
||||
"cacert.unbundled" # some services require unbundled /etc/ssl/certs
|
||||
cryptsetup
|
||||
dig
|
||||
efibootmgr
|
||||
fatresize
|
||||
fd
|
||||
file
|
||||
gawk
|
||||
git
|
||||
gptfdisk
|
||||
hdparm
|
||||
htop
|
||||
iftop
|
||||
inetutils # for telnet
|
||||
iotop
|
||||
iptables
|
||||
jq
|
||||
killall
|
||||
lsof
|
||||
nano
|
||||
netcat
|
||||
nethogs
|
||||
nmap
|
||||
openssl
|
||||
parted
|
||||
pciutils
|
||||
powertop
|
||||
pstree
|
||||
ripgrep
|
||||
screen
|
||||
smartmontools
|
||||
socat
|
||||
strace
|
||||
subversion
|
||||
tcpdump
|
||||
tree
|
||||
usbutils
|
||||
wget
|
||||
;
|
||||
};
|
||||
sysadminExtraPkgs = {
|
||||
# application-specific packages
|
||||
inherit (pkgs)
|
||||
backblaze-b2
|
||||
duplicity
|
||||
sqlite # to debug sqlite3 databases
|
||||
;
|
||||
};
|
||||
|
||||
iphonePkgs = {
|
||||
inherit (pkgs)
|
||||
ifuse
|
||||
ipfs
|
||||
libimobiledevice
|
||||
;
|
||||
};
|
||||
|
||||
tuiPkgs = {
|
||||
inherit (pkgs)
|
||||
aerc # email client
|
||||
offlineimap # email mailox sync
|
||||
visidata # TUI spreadsheet viewer/editor
|
||||
w3m
|
||||
;
|
||||
};
|
||||
|
||||
# TODO: split these into smaller groups.
|
||||
# - transcoders (ffmpeg, imagemagick) only wanted on desko/lappy ("powerutils"?)
|
||||
consolePkgs = {
|
||||
inherit (pkgs)
|
||||
cdrtools
|
||||
dmidecode
|
||||
efivar
|
||||
flashrom
|
||||
fwupd
|
||||
gh # MS GitHub cli
|
||||
git # needed as a user package, for config.
|
||||
gnupg
|
||||
gocryptfs
|
||||
gopass # TODO: shouldn't be needed here
|
||||
gopass-jsonapi
|
||||
imagemagick
|
||||
kitty # TODO: move to GUI, but `ssh servo` from kitty sets `TERM=xterm-kitty` in the remove and breaks things
|
||||
libsecret # for managing user keyrings
|
||||
lm_sensors # for sensors-detect
|
||||
lshw
|
||||
ffmpeg
|
||||
memtester
|
||||
neovim
|
||||
# nettools
|
||||
# networkmanager
|
||||
nixpkgs-review
|
||||
# nixos-generators
|
||||
nmon
|
||||
# node2nix
|
||||
# oathToolkit # for oathtool
|
||||
# ponymix
|
||||
pulsemixer
|
||||
python3
|
||||
ripgrep # needed as a user package, for config.
|
||||
rsync
|
||||
# python3Packages.eyeD3 # music tagging
|
||||
sane-scripts
|
||||
sequoia
|
||||
snapper
|
||||
sops
|
||||
sox
|
||||
speedtest-cli
|
||||
# ssh-to-age
|
||||
sudo
|
||||
# tageditor # music tagging
|
||||
unar
|
||||
wireguard-tools
|
||||
xdg-utils # for xdg-open
|
||||
# yarn
|
||||
# youtube-dl
|
||||
yt-dlp
|
||||
zsh
|
||||
;
|
||||
};
|
||||
|
||||
guiPkgs = {
|
||||
inherit (flattenedPkgs)
|
||||
# celluloid # mpv frontend
|
||||
clinfo
|
||||
emote
|
||||
evince # works on phosh
|
||||
|
||||
# { pkg = fluffychat-moby; persist.plaintext = [ ".local/share/chat.fluffy.fluffychat" ]; } # TODO: ship normal fluffychat on non-moby?
|
||||
|
||||
# foliate # e-book reader
|
||||
|
||||
# XXX by default fractal stores its state in ~/.local/share/<UUID>.
|
||||
# after logging in, manually change ~/.local/share/keyrings/... to point it to some predictable subdir.
|
||||
# then reboot (so that libsecret daemon re-loads the keyring...?)
|
||||
# { pkg = fractal-latest; persist.private = [ ".local/share/fractal" ]; }
|
||||
# { pkg = fractal-next; persist.private = [ ".local/share/fractal" ]; }
|
||||
|
||||
# "gnome.cheese"
|
||||
"gnome.dconf-editor"
|
||||
# gnome-feeds # RSS reader (with claimed mobile support)
|
||||
"gnome.file-roller"
|
||||
# "gnome.gnome-maps" # works on phosh
|
||||
"gnome.nautilus"
|
||||
# gnome-podcasts
|
||||
"gnome.gnome-system-monitor"
|
||||
# "gnome.gnome-terminal" # works on phosh
|
||||
# "gnome.gnome-weather"
|
||||
gpodder
|
||||
gthumb
|
||||
jellyfin-media-player
|
||||
# lollypop
|
||||
# mpv
|
||||
networkmanagerapplet
|
||||
# newsflash
|
||||
nheko
|
||||
pavucontrol
|
||||
# picard # music tagging
|
||||
playerctl
|
||||
# "libsForQt5.plasmatube" # Youtube player
|
||||
soundconverter
|
||||
sublime-music
|
||||
# tdesktop # broken on phosh
|
||||
# tokodon
|
||||
vlc
|
||||
# pleroma client (Electron). input is broken on phosh. TODO(2023/02/02): fix electron19 input (insecure)
|
||||
# whalebird
|
||||
xterm # broken on phosh
|
||||
;
|
||||
};
|
||||
desktopGuiPkgs = {
|
||||
inherit (flattenedPkgs)
|
||||
audacity
|
||||
brave # for the integrated wallet -- as a backup
|
||||
chromium
|
||||
dino
|
||||
electrum
|
||||
element-desktop
|
||||
font-manager
|
||||
gajim # XMPP client
|
||||
gimp # broken on phosh
|
||||
"gnome.gnome-disk-utility"
|
||||
# "gnome.totem" # video player, supposedly supports UPnP
|
||||
handbrake
|
||||
hase
|
||||
inkscape
|
||||
kdenlive
|
||||
kid3 # audio tagging
|
||||
krita
|
||||
libreoffice-fresh
|
||||
mumble
|
||||
obsidian
|
||||
slic3r
|
||||
steam
|
||||
wireshark # could maybe ship the cli as sysadmin pkg
|
||||
;
|
||||
};
|
||||
x86GuiPkgs = {
|
||||
inherit (pkgs)
|
||||
discord
|
||||
|
||||
# kaiteki # Pleroma client
|
||||
# gnome.zenity # for kaiteki (it will use qarma, kdialog, or zenity)
|
||||
# gpt2tc # XXX: unreliable mirror
|
||||
|
||||
# logseq # Personal Knowledge Management
|
||||
losslesscut-bin
|
||||
makemkv
|
||||
monero-gui
|
||||
signal-desktop
|
||||
spotify
|
||||
tor-browser-bundle-bin
|
||||
zecwallet-lite
|
||||
;
|
||||
};
|
||||
|
||||
# packages not part of any package set; not enabled by default
|
||||
otherPkgs = {
|
||||
inherit (pkgs)
|
||||
lemmy-server
|
||||
mx-sanebot
|
||||
stepmania
|
||||
;
|
||||
};
|
||||
|
||||
# define -- but don't enable -- the packages in some attrset.
|
||||
declarePkgs = pkgsAsAttrs: mapAttrs (_n: p: {
|
||||
# no need to actually define the package here: it's defaulted
|
||||
# package = mkDefault p;
|
||||
}) pkgsAsAttrs;
|
||||
in
|
||||
{
|
||||
|
||||
imports = [
|
||||
./aerc.nix
|
||||
./assorted.nix
|
||||
./git.nix
|
||||
./gnome-feeds.nix
|
||||
./gpodder.nix
|
||||
./imagemagick.nix
|
||||
./jellyfin-media-player.nix
|
||||
./kitty
|
||||
./libreoffice.nix
|
||||
./mpv.nix
|
||||
@@ -269,6 +17,7 @@ in
|
||||
./offlineimap.nix
|
||||
./ripgrep.nix
|
||||
./splatmoji.nix
|
||||
./steam.nix
|
||||
./sublime-music.nix
|
||||
./vlc.nix
|
||||
./web-browser.nix
|
||||
@@ -278,146 +27,8 @@ in
|
||||
];
|
||||
|
||||
config = {
|
||||
sane.programs = mkMerge [
|
||||
(declarePkgs consolePkgs)
|
||||
(declarePkgs desktopGuiPkgs)
|
||||
(declarePkgs guiPkgs)
|
||||
(declarePkgs iphonePkgs)
|
||||
(declarePkgs sysadminPkgs)
|
||||
(declarePkgs sysadminExtraPkgs)
|
||||
(declarePkgs tuiPkgs)
|
||||
(declarePkgs x86GuiPkgs)
|
||||
(declarePkgs otherPkgs)
|
||||
{
|
||||
# link the various package sets into their own meta packages
|
||||
consoleUtils = {
|
||||
package = null;
|
||||
suggestedPrograms = attrNames consolePkgs;
|
||||
};
|
||||
desktopGuiApps = {
|
||||
package = null;
|
||||
suggestedPrograms = attrNames desktopGuiPkgs;
|
||||
};
|
||||
guiApps = {
|
||||
package = null;
|
||||
suggestedPrograms = (attrNames guiPkgs)
|
||||
++ [ "web-browser" ]
|
||||
++ [ "tuiApps" ]
|
||||
++ optional (pkgs.system == "x86_64-linux") "x86GuiApps";
|
||||
};
|
||||
iphoneUtils = {
|
||||
package = null;
|
||||
suggestedPrograms = attrNames iphonePkgs;
|
||||
};
|
||||
sysadminUtils = {
|
||||
package = null;
|
||||
suggestedPrograms = attrNames sysadminPkgs;
|
||||
};
|
||||
sysadminExtraUtils = {
|
||||
package = null;
|
||||
suggestedPrograms = attrNames sysadminExtraPkgs;
|
||||
};
|
||||
tuiApps = {
|
||||
package = null;
|
||||
suggestedPrograms = attrNames tuiPkgs;
|
||||
};
|
||||
x86GuiApps = {
|
||||
package = null;
|
||||
suggestedPrograms = attrNames x86GuiPkgs;
|
||||
};
|
||||
}
|
||||
{
|
||||
# nontrivial package definitions
|
||||
|
||||
dino.persist.private = [ ".local/share/dino" ];
|
||||
|
||||
# creds, but also 200 MB of node modules, etc
|
||||
discord.persist.private = [ ".config/discord" ];
|
||||
|
||||
# creds/session keys, etc
|
||||
element-desktop.persist.private = [ ".config/Element" ];
|
||||
|
||||
# `emote` will show a first-run dialog based on what's in this directory.
|
||||
# mostly, it just keeps a LRU of previously-used emotes to optimize display order.
|
||||
# TODO: package [smile](https://github.com/mijorus/smile) for probably a better mobile experience.
|
||||
emote.persist.plaintext = [ ".local/share/Emote" ];
|
||||
|
||||
# MS GitHub stores auth token in .config
|
||||
# TODO: we can populate gh's stuff statically; it even lets us use the same oauth across machines
|
||||
gh.persist.private = [ ".config/gh" ];
|
||||
|
||||
ghostscript = {}; # used by imagemagick
|
||||
|
||||
imagemagick = {
|
||||
package = pkgs.imagemagick.override {
|
||||
ghostscriptSupport = true;
|
||||
};
|
||||
suggestedPrograms = [ "ghostscript" ];
|
||||
};
|
||||
|
||||
# jellyfin stores things in a bunch of directories: this one persists auth info.
|
||||
# it *might* be possible to populate this externally (it's Qt stuff), but likely to
|
||||
# be fragile and take an hour+ to figure out.
|
||||
jellyfin-media-player.persist.plaintext = [ ".local/share/Jellyfin Media Player" ];
|
||||
|
||||
# actual monero blockchain (not wallet/etc; safe to delete, just slow to regenerate)
|
||||
# XXX: is it really safe to persist this? it doesn't have info that could de-anonymize if captured?
|
||||
monero-gui.persist.plaintext = [ ".bitmonero" ];
|
||||
|
||||
mumble.persist.private = [ ".local/share/Mumble" ];
|
||||
|
||||
# not strictly necessary, but allows caching articles; offline use, etc.
|
||||
nheko.persist.private = [
|
||||
".config/nheko" # config file (including client token)
|
||||
".cache/nheko" # media cache
|
||||
".local/share/nheko" # per-account state database
|
||||
];
|
||||
|
||||
# settings (electron app)
|
||||
obsidian.persist.plaintext = [ ".config/obsidian" ];
|
||||
|
||||
# creds, media
|
||||
signal-desktop.persist.private = [ ".config/Signal" ];
|
||||
|
||||
# printer/filament settings
|
||||
slic3r.persist.plaintext = [ ".Slic3r" ];
|
||||
|
||||
# creds, widevine .so download. TODO: could easily manage these statically.
|
||||
spotify.persist.plaintext = [ ".config/spotify" ];
|
||||
|
||||
steam.persist.plaintext = [
|
||||
".steam"
|
||||
".local/share/Steam"
|
||||
];
|
||||
|
||||
tdesktop.persist.private = [ ".local/share/TelegramDesktop" ];
|
||||
|
||||
tokodon.persist.private = [ ".cache/KDE/tokodon" ];
|
||||
|
||||
# hardenedMalloc solves a crash at startup
|
||||
# TODO 2023/02/02: is this safe to remove yet?
|
||||
tor-browser-bundle-bin.package = pkgs.tor-browser-bundle-bin.override {
|
||||
useHardenedMalloc = false;
|
||||
};
|
||||
|
||||
whalebird.persist.private = [ ".config/Whalebird" ];
|
||||
|
||||
yarn.persist.plaintext = [ ".cache/yarn" ];
|
||||
|
||||
# zcash coins. safe to delete, just slow to regenerate (10-60 minutes)
|
||||
zecwallet-lite.persist.private = [ ".zcash" ];
|
||||
}
|
||||
];
|
||||
|
||||
# XXX: this might not be necessary. try removing this and cacert.unbundled (servo)?
|
||||
environment.etc."ssl/certs".source = "${pkgs.cacert.unbundled}/etc/ssl/certs/*";
|
||||
|
||||
# steam requires system-level config for e.g. firewall or controller support
|
||||
programs.steam = mkIf config.sane.programs.steam.enabled {
|
||||
enable = true;
|
||||
# not sure if needed: stole this whole snippet from the wiki
|
||||
remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play
|
||||
dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server
|
||||
};
|
||||
};
|
||||
}
|
||||
|
10
hosts/common/programs/imagemagick.nix
Normal file
10
hosts/common/programs/imagemagick.nix
Normal file
@@ -0,0 +1,10 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
sane.programs.imagemagick = {
|
||||
package = pkgs.imagemagick.override {
|
||||
ghostscriptSupport = true;
|
||||
};
|
||||
suggestedPrograms = [ "ghostscript" ];
|
||||
};
|
||||
sane.programs.ghostscript = {};
|
||||
}
|
13
hosts/common/programs/jellyfin-media-player.nix
Normal file
13
hosts/common/programs/jellyfin-media-player.nix
Normal file
@@ -0,0 +1,13 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
sane.programs.jellyfin-media-player = {
|
||||
# package = pkgs.jellyfin-media-player;
|
||||
package = pkgs.jellyfin-media-player-qt6;
|
||||
|
||||
# jellyfin stores things in a bunch of directories: this one persists auth info.
|
||||
# it *might* be possible to populate this externally (it's Qt stuff), but likely to
|
||||
# be fragile and take an hour+ to figure out.
|
||||
persist.plaintext = [ ".local/share/Jellyfin Media Player" ];
|
||||
};
|
||||
}
|
16
hosts/common/programs/steam.nix
Normal file
16
hosts/common/programs/steam.nix
Normal file
@@ -0,0 +1,16 @@
|
||||
{ config, lib, ...}:
|
||||
{
|
||||
sane.programs.steam = {
|
||||
persist.plaintext = [
|
||||
".steam"
|
||||
".local/share/Steam"
|
||||
];
|
||||
};
|
||||
# steam requires system-level config for e.g. firewall or controller support
|
||||
programs.steam = lib.mkIf config.sane.programs.steam.enabled {
|
||||
enable = true;
|
||||
# not sure if needed: stole this whole snippet from the wiki
|
||||
remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play
|
||||
dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server
|
||||
};
|
||||
}
|
@@ -56,10 +56,26 @@ let
|
||||
nixExtensions = concatMap (ext: optional ext.enable ext.package) (attrValues cfg.addons);
|
||||
|
||||
extraPolicies = {
|
||||
FirefoxHome = {
|
||||
Search = true;
|
||||
Pocket = false;
|
||||
Snippets = false;
|
||||
TopSites = false;
|
||||
Highlights = false;
|
||||
};
|
||||
NoDefaultBookmarks = true;
|
||||
OfferToSaveLogins = false;
|
||||
OfferToSaveLoginsDefault = false;
|
||||
PasswordManagerEnabled = false;
|
||||
SearchEngines = {
|
||||
Default = "DuckDuckGo";
|
||||
};
|
||||
UserMessaging = {
|
||||
ExtensionRecommendations = false;
|
||||
SkipOnboarding = true;
|
||||
};
|
||||
|
||||
# these were taken from Librewolf
|
||||
AppUpdateURL = "https://localhost";
|
||||
DisableAppUpdate = true;
|
||||
OverrideFirstRunPage = "";
|
||||
@@ -88,6 +104,7 @@ let
|
||||
# };
|
||||
# NewTabPage = true;
|
||||
};
|
||||
# extraPrefs = ...
|
||||
};
|
||||
|
||||
addonOpts = types.submodule {
|
||||
|
@@ -138,7 +138,7 @@ in
|
||||
}
|
||||
''
|
||||
+ lib.optionalString cfg.showDeadlines ''
|
||||
${pkgs.sane-scripts}/bin/sane-deadlines
|
||||
${pkgs.sane-scripts.deadlines}/bin/sane-deadlines
|
||||
''
|
||||
+ ''
|
||||
# auto-cd into any of these dirs by typing them and pressing 'enter':
|
||||
|
@@ -30,4 +30,15 @@ in
|
||||
})
|
||||
(globalKeys ++ domainKeys)
|
||||
);
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings.PermitRootLogin = "no";
|
||||
settings.PasswordAuthentication = false;
|
||||
};
|
||||
sane.ports.ports."22" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
description = lib.mkDefault "colin-ssh";
|
||||
};
|
||||
}
|
||||
|
@@ -129,11 +129,5 @@ in
|
||||
enable = true;
|
||||
wheelNeedsPassword = false;
|
||||
};
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings.PermitRootLogin = "no";
|
||||
settings.PasswordAuthentication = false;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@@ -90,7 +90,7 @@ in
|
||||
};
|
||||
sane.gui.sxmo.terminal = mkOption {
|
||||
# type = types.nullOr (types.enum [ "foot" "st" "vte" ]);
|
||||
type = types.nullOr types.string;
|
||||
type = types.nullOr types.str;
|
||||
default = "foot";
|
||||
description = ''
|
||||
name of terminal to use for sxmo_terminal.sh.
|
||||
@@ -99,7 +99,7 @@ in
|
||||
};
|
||||
sane.gui.sxmo.keyboard = mkOption {
|
||||
# type = types.nullOr (types.enum ["wvkbd"])
|
||||
type = types.nullOr types.string;
|
||||
type = types.nullOr types.str;
|
||||
default = "wvkbd";
|
||||
description = ''
|
||||
name of on-screen-keyboard to use for sxmo_keyboard.sh.
|
||||
@@ -108,7 +108,7 @@ in
|
||||
'';
|
||||
};
|
||||
sane.gui.sxmo.settings = mkOption {
|
||||
type = types.attrsOf types.string;
|
||||
type = types.attrsOf types.str;
|
||||
default = {};
|
||||
description = ''
|
||||
environment variables used to configure sxmo.
|
||||
|
@@ -26,7 +26,7 @@ in
|
||||
type = types.bool;
|
||||
};
|
||||
sane.nixcache.substituters = mkOption {
|
||||
type = types.listOf types.string;
|
||||
type = types.listOf types.str;
|
||||
default =
|
||||
# TODO: make these blacklisted entries injectable
|
||||
(lib.optional (hostName != "servo") "https://nixcache.uninsane.org")
|
||||
|
@@ -10,7 +10,7 @@
|
||||
};
|
||||
|
||||
config = lib.mkIf config.sane.roles.ac {
|
||||
sane.yggdrasil.enable = true;
|
||||
services.i2p.enable = true;
|
||||
# sane.yggdrasil.enable = true;
|
||||
# services.i2p.enable = true;
|
||||
};
|
||||
}
|
||||
|
@@ -33,6 +33,11 @@ in
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
sane.services.wg-home.enableWan = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "whether to make this port visible on the WAN";
|
||||
};
|
||||
sane.services.wg-home.ip = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
@@ -50,7 +55,12 @@ in
|
||||
# this config defines both the endpoint (server) and client configs
|
||||
|
||||
# for convenience, have both the server and client use the same port for their wireguard connections.
|
||||
networking.firewall.allowedUDPPorts = [ 51820 ];
|
||||
sane.ports.ports."51820" = {
|
||||
protocol = [ "udp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = cfg.enableWan;
|
||||
description = "colin-wireguard";
|
||||
};
|
||||
networking.wireguard.interfaces.wg-home = {
|
||||
listenPort = 51820;
|
||||
privateKeyFile = "/run/wg-home.priv";
|
||||
|
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 1369733,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "Every company has a story. Learn the playbooks that built the world’s greatest companies — and how you can apply them as a founder, operator, or investor.",
|
||||
"favicon": null,
|
||||
"hubs": [],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 173,
|
||||
"last_seen": "2023-01-11T15:26:37.515527+00:00",
|
||||
"last_updated": "2022-12-19T07:22:28+00:00",
|
||||
"score": 18,
|
||||
"self_url": "https://acquired.libsyn.com/rss",
|
||||
"site_name": null,
|
||||
"site_url": null,
|
||||
"title": "Acquired",
|
||||
"url": "https://acquired.libsyn.com/rss",
|
||||
"velocity": 0.066,
|
||||
"version": "rss20"
|
||||
}
|
@@ -1,21 +1,23 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 443732,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "Ben and David are joined by expert founders and investors \u2014 writing the next generation of great company stories in real-time.\n\nWe go behind the scenes on their journeys and bring back emerging insights and lessons that are useful for anyone in the tech and investing ecosystems.\n\nAcquired covers yesterday. ACQ2 covers tomorrow.",
|
||||
"content_length": 567579,
|
||||
"content_type": "text/xml; charset=utf-8",
|
||||
"description": "ACQ2 is Ben and David's conversations with expert founders and investors.",
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [],
|
||||
"hubs": [
|
||||
"https://pubsubhubbub.appspot.com/"
|
||||
],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 92,
|
||||
"last_updated": "2023-03-02T17:03:15+00:00",
|
||||
"score": 10,
|
||||
"self_url": "https://acquiredlpbonussecretsecret.libsyn.com/",
|
||||
"site_name": "ACQ2 by Acquired",
|
||||
"site_url": "https://acquiredlpbonussecretsecret.libsyn.com",
|
||||
"title": "ACQ2 by Acquired",
|
||||
"url": "https://acquiredlpbonussecretsecret.libsyn.com",
|
||||
"velocity": 0.057,
|
||||
"is_push": true,
|
||||
"item_count": 91,
|
||||
"last_updated": "2023-05-09T06:51:48+00:00",
|
||||
"score": 24,
|
||||
"self_url": "https://feeds.transistor.fm/acq2",
|
||||
"site_name": "ACQ2: The Acquired Interviews",
|
||||
"site_url": "https://feeds.transistor.fm",
|
||||
"title": "ACQ2: The Acquired Interviews",
|
||||
"url": "https://feeds.transistor.fm/acq2",
|
||||
"velocity": 0.054,
|
||||
"version": "rss20"
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 1579416,
|
||||
"content_type": "text/xml; charset=utf-8",
|
||||
"description": "Every company has a story.",
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [
|
||||
"https://pubsubhubbub.appspot.com/"
|
||||
],
|
||||
"is_podcast": true,
|
||||
"is_push": true,
|
||||
"item_count": 178,
|
||||
"last_updated": "2023-05-30T05:02:40+00:00",
|
||||
"score": 24,
|
||||
"self_url": "https://feeds.transistor.fm/acquired",
|
||||
"site_name": "",
|
||||
"site_url": "https://feeds.transistor.fm",
|
||||
"title": "Acquired",
|
||||
"url": "https://feeds.transistor.fm/acquired",
|
||||
"velocity": 0.064,
|
||||
"version": "rss20"
|
||||
}
|
@@ -1,21 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 235911,
|
||||
"content_length": 145311,
|
||||
"content_type": "application/xml; charset=utf-8",
|
||||
"description": "<p>The Portal is an exploration into discovery, including conversations with thought leaders. Host Eric Weinstein, Managing Director of Thiel Capital, brings his unique expertise and diverse roster of guests for a wide range of discussions, including science, culture, business, and capitalism. The show will feature people whose lives demonstrate that portals into what we would normally consider impossible, are indeed possible. Guests include presidential candidate Andrew Yang, NY Times bestselling author Sam Harris, and retired Navy Seal and creator of the hit business podcast Jocko Willink.</p>",
|
||||
"favicon": null,
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 44,
|
||||
"last_seen": "2023-01-11T14:47:44.995855+00:00",
|
||||
"last_updated": "2020-12-02T07:50:55+00:00",
|
||||
"score": -12,
|
||||
"self_url": "https://www.omnycontent.com/d/playlist/9b7dacdf-a925-4f95-84dc-ac46003451ff/1713c520-edb6-43a3-b1b9-acb8002fdae7/58e33a0c-f86b-41c5-a11c-acb8002fdaf5/podcast.rss",
|
||||
"site_name": null,
|
||||
"site_url": null,
|
||||
"score": 8,
|
||||
"self_url": "",
|
||||
"site_name": "",
|
||||
"site_url": "",
|
||||
"title": "The Portal",
|
||||
"url": "https://www.omnycontent.com/d/playlist/9b7dacdf-a925-4f95-84dc-ac46003451ff/1713c520-edb6-43a3-b1b9-acb8002fdae7/58e33a0c-f86b-41c5-a11c-acb8002fdaf5/podcast.rss",
|
||||
"url": "https://feed.cdnstream1.com/zjb/feed/download/d9/8a/71/d98a71ac-d1a3-4d92-ab64-64b4ff3192d1.xml",
|
||||
"velocity": 0.082,
|
||||
"version": "rss20"
|
||||
}
|
||||
}
|
@@ -2,12 +2,14 @@
|
||||
|
||||
{
|
||||
imports = [
|
||||
./dns.nix
|
||||
./feeds.nix
|
||||
./fs
|
||||
./ids.nix
|
||||
./programs.nix
|
||||
./image.nix
|
||||
./persist
|
||||
./ports.nix
|
||||
./services
|
||||
./sops.nix
|
||||
./ssh.nix
|
||||
|
146
modules/dns.nix
Normal file
146
modules/dns.nix
Normal file
@@ -0,0 +1,146 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with builtins;
|
||||
let
|
||||
cfg = config.sane.dns;
|
||||
toml = pkgs.formats.toml { };
|
||||
recordFormatters = {
|
||||
# quote rules for zone files:
|
||||
# - any character may be encoded by `\DDD`, where `DDD` represents its ascii value in base 8.
|
||||
# - any non-digit `X` may be encoded by `\X`.
|
||||
# - stated in: <https://www.ietf.org/rfc/rfc1035.txt>: 5.1 Format
|
||||
# - visible in <trust-dns:crates/proto/src/serialize/txt/zone_lex.rs:escape_seq>
|
||||
# for us, we can just replace `\` => `\\ and `"` -> `\"`
|
||||
TXT = value: "\"" + (lib.escape [ "\\" "\"" ] value) + "\"";
|
||||
};
|
||||
# proto: "INET", etc
|
||||
# rrtype: "TXT", "A", "CNAME", etc
|
||||
fmtRecord = proto: rrtype: name: value:
|
||||
let
|
||||
formatter = recordFormatters."${rrtype}" or lib.id;
|
||||
in
|
||||
"${name}\t${proto}\t${rrtype}\t${formatter value}";
|
||||
fmtRecordList = proto: rrtype: name: values: concatStringsSep
|
||||
"\n"
|
||||
(map (fmtRecord proto rrtype name) values)
|
||||
;
|
||||
fmtRecordAttrs = proto: rrtype: rrAttrs:
|
||||
concatStringsSep
|
||||
"\n"
|
||||
(
|
||||
attrValues (
|
||||
mapAttrs
|
||||
(name: fmtRecordList proto rrtype name)
|
||||
rrAttrs
|
||||
)
|
||||
);
|
||||
# format other .zone files to include into this one
|
||||
fmtIncludes = paths: concatStringsSep
|
||||
"\n"
|
||||
(map (path: "$INCLUDE ${path}") paths);
|
||||
|
||||
genZone = zcfg: ''
|
||||
$TTL ${toString zcfg.TTL}
|
||||
${fmtRecordAttrs "IN" "SOA" zcfg.inet.SOA}
|
||||
${fmtRecordAttrs "IN" "A" zcfg.inet.A}
|
||||
${fmtRecordAttrs "IN" "CNAME" zcfg.inet.CNAME}
|
||||
${fmtRecordAttrs "IN" "MX" zcfg.inet.MX}
|
||||
${fmtRecordAttrs "IN" "NS" zcfg.inet.NS}
|
||||
${fmtRecordAttrs "IN" "SRV" zcfg.inet.SRV}
|
||||
${fmtRecordAttrs "IN" "TXT" zcfg.inet.TXT}
|
||||
${fmtIncludes zcfg.include}
|
||||
${zcfg.extraConfig}
|
||||
'';
|
||||
|
||||
# (listOf ty) type which also accepts single-assignment of `ty`.
|
||||
# it's used to allow the user to write:
|
||||
# CNAME."foo" = "bar";
|
||||
# as shorthand for
|
||||
# CNAME."foo" = [ "bar" ];
|
||||
listOrUnit = with lib; ty: types.coercedTo ty (elem: [ elem ]) (types.listOf ty);
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sane.dns = with lib; {
|
||||
zones = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = "zone name. defaults to the attribute name in zones";
|
||||
default = null;
|
||||
};
|
||||
TTL = mkOption {
|
||||
type = types.int;
|
||||
description = "default TTL";
|
||||
default = 3600;
|
||||
};
|
||||
include = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "paths of other zone files to $INCLUDE into this one";
|
||||
default = [];
|
||||
};
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
description = "extra lines to append to the zone file";
|
||||
default = "";
|
||||
};
|
||||
inet = {
|
||||
SOA = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "Start of Authority record(s)";
|
||||
default = {};
|
||||
};
|
||||
A = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "IPv4 address record(s)";
|
||||
default = {};
|
||||
};
|
||||
CNAME = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "canonical name record(s)";
|
||||
default = {};
|
||||
};
|
||||
MX = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "mail exchanger record(s)";
|
||||
default = {};
|
||||
};
|
||||
NS = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "name server record(s)";
|
||||
default = {};
|
||||
};
|
||||
SRV = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "service record(s)";
|
||||
default = {};
|
||||
};
|
||||
TXT = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "text record(s)";
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
|
||||
file = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
instead of using the generated zone file, use the specified path (user should populate the file specified here).
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
default = {};
|
||||
description = "Declarative zone config";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
sane.services.trust-dns.zones = mapAttrs (_name: zcfg: {
|
||||
text = genZone zcfg;
|
||||
}) cfg.zones;
|
||||
};
|
||||
}
|
113
modules/ports.nix
Normal file
113
modules/ports.nix
Normal file
@@ -0,0 +1,113 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sane.ports;
|
||||
|
||||
portOpts = with lib; types.submodule {
|
||||
options = {
|
||||
protocol = mkOption {
|
||||
type = types.listOf (types.enum [ "udp" "tcp" ]);
|
||||
};
|
||||
visibleTo.lan = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
# XXX: if a service is visible to the WAN, it ends up visible to the LAN as well.
|
||||
# technically solvable (explicitly drop packets delivered from LAN IPs) but doesn't make much sense.
|
||||
};
|
||||
visibleTo.wan = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
visibleTo.ovpn = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
# XXX: behaves more or less the same as `lan` visibility.
|
||||
# OVPN passes everything by default.
|
||||
# TODO: have *this* drive what we forward from wireguard namespace to main namespace
|
||||
};
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
default = "colin-${config.net.hostName}";
|
||||
description = ''
|
||||
short description of why this port is open.
|
||||
this is shown, for example, in an upstream's UPnP status page.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# gives networking.firewall value for a given "${port}" = portCfg.
|
||||
firewallConfigForPort = port: portCfg:
|
||||
# any form of visibility means we need to open the firewall
|
||||
lib.mkIf (portCfg.visibleTo.lan || portCfg.visibleTo.wan || portCfg.visibleTo.ovpn) {
|
||||
allowedTCPPorts = lib.optional (lib.elem "tcp" portCfg.protocol) (lib.toInt port);
|
||||
allowedUDPPorts = lib.optional (lib.elem "udp" portCfg.protocol) (lib.toInt port);
|
||||
};
|
||||
in
|
||||
{
|
||||
options = with lib; {
|
||||
sane.ports = {
|
||||
openFirewall = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
};
|
||||
openUpnp = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
};
|
||||
upnpRenewInterval = mkOption {
|
||||
default = "1hr";
|
||||
type = types.str;
|
||||
description = "how frequently to renew UPnP leases";
|
||||
};
|
||||
upnpLeaseDuration = mkOption {
|
||||
default = 86400;
|
||||
type = types.int;
|
||||
description = "how long to lease UPnP ports for";
|
||||
};
|
||||
|
||||
ports = mkOption {
|
||||
type = types.attrsOf portOpts;
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
(lib.mkIf cfg.openFirewall {
|
||||
networking.firewall = lib.mkMerge (lib.mapAttrsToList firewallConfigForPort cfg.ports);
|
||||
})
|
||||
(lib.mkIf cfg.openUpnp {
|
||||
systemd.services.upnp-forwards = {
|
||||
description = "forward ports from upstream gateway to this host";
|
||||
serviceConfig.Type = "oneshot";
|
||||
restartTriggers = [(builtins.toJSON cfg)];
|
||||
|
||||
after = [ "network.target" ];
|
||||
script =
|
||||
let
|
||||
portFwd = "${pkgs.sane-scripts.ip-port-forward}/bin/sane-ip-port-forward";
|
||||
forwardsPerCfg = lib.mapAttrsToList
|
||||
(port: portCfg: lib.optionals portCfg.visibleTo.wan
|
||||
(
|
||||
lib.optional (lib.elem "udp" portCfg.protocol) "udp:${port}:${portCfg.description}"
|
||||
++ lib.optional (lib.elem "tcp" portCfg.protocol) "tcp:${port}:${portCfg.description}"
|
||||
)
|
||||
)
|
||||
cfg.ports;
|
||||
forwards = lib.flatten forwardsPerCfg;
|
||||
in ''
|
||||
${portFwd} -v -d ${builtins.toString cfg.upnpLeaseDuration} \
|
||||
${lib.escapeShellArgs forwards}
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.timers.upnp-forwards = {
|
||||
wantedBy = [ "network.target" ];
|
||||
timerConfig = {
|
||||
OnStartupSec = "1min";
|
||||
OnUnitActiveSec = cfg.upnpRenewInterval;
|
||||
};
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
@@ -5,8 +5,9 @@ let
|
||||
cfg = config.sane.services.dyn-dns;
|
||||
getIp = pkgs.writeShellScript "dyn-dns-query-wan" ''
|
||||
# preferred method and fallback
|
||||
${pkgs.sane-scripts}/bin/sane-ip-check-router-wan || \
|
||||
${pkgs.sane-scripts}/bin/sane-ip-check
|
||||
# OPNsense router broadcasts its UPnP endpoint every 30s
|
||||
timeout 60 ${pkgs.sane-scripts.ip-check-upnp}/bin/sane-ip-check-upnp || \
|
||||
${pkgs.sane-scripts.ip-check}/bin/sane-ip-check
|
||||
'';
|
||||
in
|
||||
{
|
||||
|
@@ -7,50 +7,6 @@ with lib;
|
||||
let
|
||||
cfg = config.sane.services.trust-dns;
|
||||
toml = pkgs.formats.toml { };
|
||||
recordFormatters = {
|
||||
# quote rules for zone files:
|
||||
# - any character may be encoded by `\DDD`, where `DDD` represents its ascii value in base 8.
|
||||
# - any non-digit `X` may be encoded by `\X`.
|
||||
# - stated in: <https://www.ietf.org/rfc/rfc1035.txt>: 5.1 Format
|
||||
# - visible in <trust-dns:crates/proto/src/serialize/txt/zone_lex.rs:escape_seq>
|
||||
# for us, we can just replace `\` => `\\ and `"` -> `\"`
|
||||
TXT = value: "\"" + (lib.escape [ "\\" "\"" ] value) + "\"";
|
||||
};
|
||||
fmtRecord = proto: rrtype: name: value:
|
||||
let
|
||||
formatter = recordFormatters."${rrtype}" or lib.id;
|
||||
in
|
||||
"${name}\t${proto}\t${rrtype}\t${formatter value}";
|
||||
fmtRecordList = proto: rrtype: name: values: concatStringsSep
|
||||
"\n"
|
||||
(map (fmtRecord proto rrtype name) values)
|
||||
;
|
||||
fmtRecordAttrs = proto: rrtype: rrAttrs:
|
||||
concatStringsSep
|
||||
"\n"
|
||||
(
|
||||
attrValues (
|
||||
mapAttrs
|
||||
(name: fmtRecordList proto rrtype name)
|
||||
rrAttrs
|
||||
)
|
||||
);
|
||||
fmtIncludes = paths: concatStringsSep
|
||||
"\n"
|
||||
(map (path: "$INCLUDE ${path}") paths);
|
||||
|
||||
genZone = zcfg: ''
|
||||
$TTL ${toString zcfg.TTL}
|
||||
${fmtRecordAttrs "IN" "SOA" zcfg.inet.SOA}
|
||||
${fmtRecordAttrs "IN" "A" zcfg.inet.A}
|
||||
${fmtRecordAttrs "IN" "CNAME" zcfg.inet.CNAME}
|
||||
${fmtRecordAttrs "IN" "MX" zcfg.inet.MX}
|
||||
${fmtRecordAttrs "IN" "NS" zcfg.inet.NS}
|
||||
${fmtRecordAttrs "IN" "SRV" zcfg.inet.SRV}
|
||||
${fmtRecordAttrs "IN" "TXT" zcfg.inet.TXT}
|
||||
${fmtIncludes zcfg.include}
|
||||
${zcfg.extraConfig}
|
||||
'';
|
||||
|
||||
configFile = toml.generate "trust-dns.toml" {
|
||||
listen_addrs_ipv4 = cfg.listenAddrsIPv4;
|
||||
@@ -58,20 +14,10 @@ let
|
||||
mapAttrs (zname: zcfg: rec {
|
||||
zone = if zcfg.name == null then zname else zcfg.name;
|
||||
zone_type = "Primary";
|
||||
file = if zcfg.file == null then
|
||||
pkgs.writeText "${zone}.zone" (genZone zcfg)
|
||||
else
|
||||
zcfg.file;
|
||||
file = zcfg.file;
|
||||
}) cfg.zones
|
||||
);
|
||||
};
|
||||
|
||||
# (listOf ty) type which also accepts single-assignment of `ty`.
|
||||
# it's used to allow the user to write:
|
||||
# CNAME."foo" = "bar";
|
||||
# as shorthand for
|
||||
# CNAME."foo" = [ "bar" ];
|
||||
listOrUnit = ty: types.coercedTo ty (elem: [ elem ]) (types.listOf ty);
|
||||
in
|
||||
{
|
||||
options = {
|
||||
@@ -80,6 +26,14 @@ in
|
||||
default = false;
|
||||
type = types.bool;
|
||||
};
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.trust-dns;
|
||||
description = ''
|
||||
trust-dns package to use.
|
||||
should provide bin/named, which will be invoked with --config x and --zonedir d and maybe -q.
|
||||
'';
|
||||
};
|
||||
listenAddrsIPv4 = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
@@ -89,101 +43,65 @@ in
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
zonedir = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = "/";
|
||||
description = ''
|
||||
where the `file` option in zones.* is relative to.
|
||||
'';
|
||||
};
|
||||
# reference <nixpkgs:nixos/modules/services/web-servers/nginx/vhost-options.nix>
|
||||
zones = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
type = types.attrsOf (types.submodule ({ config, name, ... }: {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = "zone name. defaults to the attribute name in zones";
|
||||
default = name;
|
||||
};
|
||||
text = mkOption {
|
||||
type = types.nullOr types.lines;
|
||||
default = null;
|
||||
};
|
||||
TTL = mkOption {
|
||||
type = types.int;
|
||||
description = "default TTL";
|
||||
default = 3600;
|
||||
};
|
||||
include = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "paths of other zone files to $INCLUDE into this one";
|
||||
default = [];
|
||||
};
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
description = "extra lines to append to the zone file";
|
||||
default = "";
|
||||
};
|
||||
inet = {
|
||||
SOA = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "Start of Authority record(s)";
|
||||
default = {};
|
||||
};
|
||||
A = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "IPv4 address record(s)";
|
||||
default = {};
|
||||
};
|
||||
CNAME = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "canonical name record(s)";
|
||||
default = {};
|
||||
};
|
||||
MX = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "mail exchanger record(s)";
|
||||
default = {};
|
||||
};
|
||||
NS = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "name server record(s)";
|
||||
default = {};
|
||||
};
|
||||
SRV = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "service record(s)";
|
||||
default = {};
|
||||
};
|
||||
TXT = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "text record(s)";
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
|
||||
file = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "instead of using the generated zone file, use the specified path";
|
||||
type = types.nullOr (types.either types.path types.str);
|
||||
description = ''
|
||||
path to a .zone file.
|
||||
if omitted, will be generated from the `text` option.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
config = {
|
||||
file = lib.mkIf (config.text != null) (pkgs.writeText "${config.name}.zone" config.text);
|
||||
};
|
||||
}));
|
||||
default = {};
|
||||
description = "Declarative zone config";
|
||||
};
|
||||
|
||||
generatedZones = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
description = "generated zone text for each zone";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
sane.services.trust-dns.generatedZones = mapAttrs (zone: zcfg: genZone zcfg) cfg.zones;
|
||||
networking.firewall.allowedTCPPorts = [ 53 ];
|
||||
networking.firewall.allowedUDPPorts = [ 53 ];
|
||||
sane.ports.ports."53" = {
|
||||
protocol = [ "udp" "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-dns-hosting";
|
||||
};
|
||||
|
||||
systemd.services.trust-dns = {
|
||||
description = "trust-dns DNS server";
|
||||
serviceConfig = {
|
||||
ExecStart =
|
||||
let
|
||||
flags = lib.optionalString cfg.quiet "-q";
|
||||
flags = lib.optional cfg.quiet "-q" ++
|
||||
lib.optionals (cfg.zonedir != null) [ "--zonedir" cfg.zonedir ];
|
||||
flagsStr = builtins.concatStringsSep " " flags;
|
||||
in ''
|
||||
${pkgs.trust-dns}/bin/named \
|
||||
${cfg.package}/bin/named \
|
||||
--config ${configFile} \
|
||||
--zonedir / ${flags}
|
||||
${flagsStr}
|
||||
'';
|
||||
Type = "simple";
|
||||
Restart = "on-failure";
|
||||
|
14
nixpatches/2023-05-31-toplevel-alsa.patch
Normal file
14
nixpatches/2023-05-31-toplevel-alsa.patch
Normal file
@@ -0,0 +1,14 @@
|
||||
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
|
||||
index d188ecdda55..69174ba7dc7 100644
|
||||
--- a/pkgs/top-level/all-packages.nix
|
||||
+++ b/pkgs/top-level/all-packages.nix
|
||||
@@ -26607,7 +26607,8 @@ with pkgs;
|
||||
|
||||
tinyalsa = callPackage ../os-specific/linux/tinyalsa { };
|
||||
|
||||
- inherit (callPackage ../os-specific/linux/alsa-project { })
|
||||
+ alsa-project = callPackage ../os-specific/linux/alsa-project { };
|
||||
+ inherit (alsa-project)
|
||||
alsa-firmware
|
||||
alsa-lib
|
||||
alsa-oss
|
31
nixpatches/2023-06-02-qt6-qtwebengine-cross.patch
Normal file
31
nixpatches/2023-06-02-qt6-qtwebengine-cross.patch
Normal file
@@ -0,0 +1,31 @@
|
||||
diff --git a/pkgs/development/libraries/qt-6/modules/qtwebengine.nix b/pkgs/development/libraries/qt-6/modules/qtwebengine.nix
|
||||
index fadbc5d2bfa..e4f2aec5a32 100644
|
||||
--- a/pkgs/development/libraries/qt-6/modules/qtwebengine.nix
|
||||
+++ b/pkgs/development/libraries/qt-6/modules/qtwebengine.nix
|
||||
@@ -97,6 +97,9 @@
|
||||
, xnu
|
||||
}:
|
||||
|
||||
+let
|
||||
+ buildPython = buildPackages.python3.withPackages (ps: with ps; [ html5lib ]);
|
||||
+in
|
||||
qtModule {
|
||||
pname = "qtwebengine";
|
||||
qtInputs = [ qtdeclarative qtwebchannel qtwebsockets qtpositioning ];
|
||||
@@ -108,7 +111,7 @@ qtModule {
|
||||
gperf
|
||||
ninja
|
||||
pkg-config
|
||||
- (python3.withPackages (ps: with ps; [ html5lib ]))
|
||||
+ buildPython
|
||||
which
|
||||
gn
|
||||
nodejs
|
||||
@@ -304,6 +307,7 @@ qtModule {
|
||||
|
||||
preConfigure = ''
|
||||
export NINJAFLAGS="-j$NIX_BUILD_CORES"
|
||||
+ export CMAKE_PREFIX_PATH="${buildPython}/bin:$CMAKE_PREFIX_PATH"
|
||||
'';
|
||||
|
||||
meta = with lib; {
|
@@ -0,0 +1,60 @@
|
||||
diff --git a/pkgs/applications/video/jellyfin-media-player/default.nix b/pkgs/applications/video/jellyfin-media-player/default.nix
|
||||
index e781f80e455..d1990294141 100644
|
||||
--- a/pkgs/applications/video/jellyfin-media-player/default.nix
|
||||
+++ b/pkgs/applications/video/jellyfin-media-player/default.nix
|
||||
@@ -1,7 +1,6 @@
|
||||
{ lib
|
||||
, fetchFromGitHub
|
||||
, fetchzip
|
||||
-, mkDerivation
|
||||
, stdenv
|
||||
, Cocoa
|
||||
, CoreAudio
|
||||
@@ -12,21 +11,20 @@
|
||||
, libGL
|
||||
, libX11
|
||||
, libXrandr
|
||||
+, libsForQt5
|
||||
, libvdpau
|
||||
, mpv
|
||||
, ninja
|
||||
, pkg-config
|
||||
, python3
|
||||
-, qtbase
|
||||
-, qtwayland
|
||||
-, qtwebchannel
|
||||
-, qtwebengine
|
||||
-, qtx11extras
|
||||
, jellyfin-web
|
||||
, withDbus ? stdenv.isLinux, dbus
|
||||
}:
|
||||
|
||||
-mkDerivation rec {
|
||||
+let
|
||||
+ inherit (libsForQt5) qtbase qtwayland qtwebchannel qtwebengine qtx11extras wrapQtAppsHook;
|
||||
+in
|
||||
+stdenv.mkDerivation rec {
|
||||
pname = "jellyfin-media-player";
|
||||
version = "1.9.1";
|
||||
|
||||
@@ -69,6 +67,7 @@ mkDerivation rec {
|
||||
ninja
|
||||
pkg-config
|
||||
python3
|
||||
+ wrapQtAppsHook
|
||||
];
|
||||
|
||||
cmakeFlags = [
|
||||
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
|
||||
index eb309c9b283..d8a718db698 100644
|
||||
--- a/pkgs/top-level/all-packages.nix
|
||||
+++ b/pkgs/top-level/all-packages.nix
|
||||
@@ -5289,7 +5289,7 @@ with pkgs;
|
||||
|
||||
jellyfin-ffmpeg = callPackage ../development/libraries/jellyfin-ffmpeg { };
|
||||
|
||||
- jellyfin-media-player = libsForQt5.callPackage ../applications/video/jellyfin-media-player {
|
||||
+ jellyfin-media-player = callPackage ../applications/video/jellyfin-media-player {
|
||||
inherit (darwin.apple_sdk.frameworks) CoreFoundation Cocoa CoreAudio MediaPlayer;
|
||||
# Disable pipewire to avoid segfault, see https://github.com/jellyfin/jellyfin-media-player/issues/341
|
||||
mpv = wrapMpv (mpv-unwrapped.override { pipewireSupport = false; }) { };
|
@@ -52,10 +52,10 @@ in [
|
||||
# TODO: why doesn't this apply?
|
||||
# ./2023-03-04-ccache-cross-fix.patch
|
||||
|
||||
# 2023-04-11: bambu-studio: init at unstable-2023-01-11
|
||||
# 2023-04-11: bambu-studio: init at 01.06.02.04
|
||||
(fetchpatch' {
|
||||
prUrl = "https://github.com/NixOS/nixpkgs/pull/206495";
|
||||
hash = "sha256-RbQzAtFTr7Nrk2YBcHpKQMYoPlFMVSXNl96B/lkKluQ=";
|
||||
hash = "sha256-jl6SZwSDhQTlpM5FyGaFU/svwTb1ySdKtvWMgsneq3A=";
|
||||
})
|
||||
|
||||
# update to newer lemmy-server.
|
||||
@@ -129,14 +129,6 @@ in [
|
||||
hash = "sha256-+g3XhmBt/udhbBDiVyfWnfXKvZTvDurlvPblQ9HYp3s=";
|
||||
})
|
||||
|
||||
(fetchpatch' {
|
||||
# 2023/05/24: merged upstream
|
||||
# hare: unstable-2023-03-15 -> unstable-2023-04-23
|
||||
# + harec: unstable-2023-02-18 -> unstable-2023-04-25
|
||||
prUrl = "https://github.com/NixOS/nixpkgs/pull/233732";
|
||||
hash = "sha256-SGDKvsMiK3Pq57JEj/MamDBX5jBXwV/E5jclKO2NAUs=";
|
||||
})
|
||||
|
||||
# (fetchpatch' {
|
||||
# title = "hare-json: init at unstable-2023-01-31";
|
||||
# saneCommit = "260f9c6ac4e3564acbceb46aa4b65fbb652f8e23";
|
||||
@@ -158,6 +150,15 @@ in [
|
||||
hash = "sha256-9XKPNg7TewicfbMgiASpYysTs5aduIVP+4onz+noc/0=";
|
||||
})
|
||||
|
||||
# make alsa-project members overridable
|
||||
./2023-05-31-toplevel-alsa.patch
|
||||
|
||||
# qt6 qtwebengine: specify `python` as buildPackages
|
||||
./2023-06-02-qt6-qtwebengine-cross.patch
|
||||
|
||||
# Jellyfin: don't build via `libsForQt5.callPackage`
|
||||
./2023-06-06-jellyfin-no-libsForQt5-callPackage.patch
|
||||
|
||||
# for raspberry pi: allow building u-boot for rpi 4{,00}
|
||||
# TODO: remove after upstreamed: https://github.com/NixOS/nixpkgs/pull/176018
|
||||
# (it's a dupe of https://github.com/NixOS/nixpkgs/pull/112677 )
|
||||
|
@@ -82,11 +82,11 @@ in {
|
||||
ibus # "error: cannot run test program while cross compiling"
|
||||
jellyfin-web # in node-dependencies-jellyfin-web: "node: command not found" (nodePackages don't cross compile)
|
||||
# libgccjit # "../../gcc-9.5.0/gcc/jit/jit-result.c:52:3: error: 'dlclose' was not declared in this scope" (needed by emacs!)
|
||||
# libsForQt5 # qtbase # make: g++: No such file or directory
|
||||
# libsForQt5 # if we emulate qt5, we're better off emulating libsForQt5 else qt complains about multiple versions of qtbase
|
||||
perlInterpreters # perl5.36.0-Module-Build perl5.36.0-Test-utf8 (see tracking issues ^)
|
||||
# qgnomeplatform
|
||||
# qtbase
|
||||
qt5 # qt5.qtx11extras fails, but we can't selectively emulate it
|
||||
# qt5 # qt5.qtbase, qt5.qtx11extras fails, but we can't selectively emulate them.
|
||||
# qt6 # "You need to set QT_HOST_PATH to cross compile Qt."
|
||||
# sequoia # "/nix/store/q8hg17w47f9xr014g36rdc2gi8fv02qc-clang-aarch64-unknown-linux-gnu-12.0.1-lib/lib/libclang.so.12: cannot open shared object file: No such file or directory"', /build/sequoia-0.27.0-vendor.tar.gz/bindgen/src/lib.rs:1975:31"
|
||||
# splatmoji
|
||||
@@ -378,8 +378,17 @@ in {
|
||||
# };
|
||||
# fixes: "src/meson.build:106:0: ERROR: Program 'glib-compile-resources' not found or not executable"
|
||||
file-roller = mvToNativeInputs [ final.glib ] super.file-roller;
|
||||
gnome-bluetooth = super.gnome-bluetooth.override {
|
||||
# fixes -msse2, -mfpmath=sse flags
|
||||
wrapGAppsHook4 = final.wrapGAppsHook;
|
||||
};
|
||||
# fixes: "meson.build:75:6: ERROR: Program 'gtk-update-icon-cache' not found or not executable"
|
||||
gnome-clocks = addNativeInputs [ final.gtk4 ] super.gnome-clocks;
|
||||
gnome-clocks = (
|
||||
addNativeInputs [ final.gtk4 ] super.gnome-clocks
|
||||
).override {
|
||||
# fixes -msse2, -mfpmath=sse flags
|
||||
wrapGAppsHook4 = final.wrapGAppsHook;
|
||||
};
|
||||
# fixes: "src/meson.build:3:0: ERROR: Program 'glib-compile-resources' not found or not executable"
|
||||
gnome-color-manager = mvToNativeInputs [ final.glib ] super.gnome-color-manager;
|
||||
# fixes "subprojects/gvc/meson.build:30:0: ERROR: Program 'glib-mkenums mkenums' not found or not executable"
|
||||
@@ -470,7 +479,7 @@ in {
|
||||
gnome-user-share = addNativeInputs [ final.glib ] super.gnome-user-share;
|
||||
# fixes: "FileNotFoundError: [Errno 2] No such file or directory: 'gtk4-update-icon-cache'"
|
||||
gnome-weather = addNativeInputs [ final.gtk4 ] super.gnome-weather;
|
||||
mutter = super.mutter.overrideAttrs (orig: {
|
||||
mutter = (super.mutter.overrideAttrs (orig: {
|
||||
nativeBuildInputs = orig.nativeBuildInputs ++ [
|
||||
final.glib # fixes "clutter/clutter/meson.build:281:0: ERROR: Program 'glib-mkenums mkenums' not found or not executable"
|
||||
final.buildPackages.gobject-introspection # allows to build without forcing `introspection=false` (which would break gnome-shell)
|
||||
@@ -481,7 +490,10 @@ in {
|
||||
];
|
||||
mesonFlags = lib.remove "-Ddocs=true" orig.mesonFlags;
|
||||
outputs = lib.remove "devdoc" orig.outputs;
|
||||
});
|
||||
})).override {
|
||||
# fixes -msse2, -mfpmath=sse flags
|
||||
wrapGAppsHook4 = final.wrapGAppsHook;
|
||||
};
|
||||
# nautilus = super.nautilus.override {
|
||||
# # fixes: "meson.build:123:0: ERROR: Dependency "libxml-2.0" not found, tried pkgconfig"
|
||||
# # new failure mode: "/nix/store/grqh2wygy9f9wp5bgvqn4im76v82zmcx-binutils-2.39/bin/ld: /nix/store/f7yr5z123d162p5457jh3wzkqm7x8yah-glib-2.74.3/lib/libglib-2.0.so: error adding symbols: file in wrong format"
|
||||
@@ -635,13 +647,18 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
jellyfin-media-player = prev.jellyfin-media-player.overrideAttrs (upstream: {
|
||||
meta = upstream.meta // {
|
||||
platforms = upstream.meta.platforms ++ [
|
||||
"aarch64-linux"
|
||||
];
|
||||
};
|
||||
});
|
||||
jellyfin-media-player = mvToBuildInputs
|
||||
[ final.libsForQt5.wrapQtAppsHook ] # this shouldn't be: but otherwise we get mixed qtbase deps
|
||||
(prev.jellyfin-media-player.overrideAttrs (upstream: {
|
||||
meta = upstream.meta // {
|
||||
platforms = upstream.meta.platforms ++ [
|
||||
"aarch64-linux"
|
||||
];
|
||||
};
|
||||
}));
|
||||
jellyfin-media-player-qt6 = mvToBuildInputs
|
||||
[ final.qt6.wrapQtAppsHook ] # otherwise the result targets x86. TODO: fix the hook in qt6 itself?
|
||||
prev.jellyfin-media-player-qt6;
|
||||
# jellyfin-web = prev.jellyfin-web.override {
|
||||
# # in node-dependencies-jellyfin-web: "node: command not found"
|
||||
# inherit (emulated) stdenv;
|
||||
@@ -677,18 +694,24 @@ in {
|
||||
# buildInputs = upstream.buildInputs ++ [ final.vala ];
|
||||
# });
|
||||
|
||||
libsForQt5 = prev.libsForQt5.overrideScope' (self: super: {
|
||||
qgpgme = super.qgpgme.overrideAttrs (orig: {
|
||||
# fix so it can find the MOC compiler
|
||||
# it looks like it might not *need* to propagate qtbase, but so far unclear
|
||||
nativeBuildInputs = orig.nativeBuildInputs ++ [ self.qtbase ];
|
||||
propagatedBuildInputs = lib.remove self.qtbase orig.propagatedBuildInputs;
|
||||
});
|
||||
phonon = super.phonon.overrideAttrs (orig: {
|
||||
# fixes "ECM (required version >= 5.60), Extra CMake Modules"
|
||||
buildInputs = orig.buildInputs ++ [ final.extra-cmake-modules ];
|
||||
});
|
||||
});
|
||||
# libsForQt5 = prev.libsForQt5.overrideScope' (self: super: {
|
||||
# qgpgme = super.qgpgme.overrideAttrs (orig: {
|
||||
# # fix so it can find the MOC compiler
|
||||
# # it looks like it might not *need* to propagate qtbase, but so far unclear
|
||||
# nativeBuildInputs = orig.nativeBuildInputs ++ [ self.qtbase ];
|
||||
# propagatedBuildInputs = lib.remove self.qtbase orig.propagatedBuildInputs;
|
||||
# });
|
||||
# phonon = super.phonon.overrideAttrs (orig: {
|
||||
# # fixes "ECM (required version >= 5.60), Extra CMake Modules"
|
||||
# buildInputs = orig.buildInputs ++ [ final.extra-cmake-modules ];
|
||||
# });
|
||||
# });
|
||||
# libsForQt5 = prev.libsForQt5.overrideScope' (self: super: {
|
||||
# # emulate all the qt5 packages, but rework `libsForQt5.callPackage` and `mkDerivation`
|
||||
# # to use non-emulated stdenv by default.
|
||||
# mkDerivation = self.mkDerivationWith final.stdenv.mkDerivation;
|
||||
# callPackage = self.newScope { inherit (self) qtCompatVersion qtModule srcs; inherit (final) stdenv; };
|
||||
# });
|
||||
|
||||
# fixes: "ar: command not found"
|
||||
# `ar` is provided by bintools
|
||||
@@ -959,34 +982,106 @@ in {
|
||||
# inherit (emulated.qt5) qtModule;
|
||||
# };
|
||||
# });
|
||||
# qt6 = prev.qt6.overrideScope' (self: super: {
|
||||
# # inherit (emulated.qt6) qtModule;
|
||||
# qtbase = super.qtbase.overrideAttrs (upstream: {
|
||||
# # cmakeFlags = upstream.cmakeFlags ++ lib.optionals (final.stdenv.buildPlatform != final.stdenv.hostPlatform) [
|
||||
# cmakeFlags = upstream.cmakeFlags ++ lib.optionals (final.stdenv.buildPlatform != final.stdenv.hostPlatform) [
|
||||
# # "-DCMAKE_CROSSCOMPILING=True" # fails to solve QT_HOST_PATH error
|
||||
# "-DQT_HOST_PATH=${final.buildPackages.qt6.full}"
|
||||
# ];
|
||||
# });
|
||||
# qtModule = args: (super.qtModule args).overrideAttrs (upstream: {
|
||||
# # the nixpkgs comment about libexec seems to be outdated:
|
||||
# # it's just that cross-compiled syncqt.pl doesn't get its #!/usr/bin/env shebang replaced.
|
||||
# preConfigure = lib.replaceStrings
|
||||
# ["${lib.getDev self.qtbase}/libexec/syncqt.pl"]
|
||||
# ["perl ${lib.getDev self.qtbase}/libexec/syncqt.pl"]
|
||||
# upstream.preConfigure;
|
||||
# });
|
||||
# # qtwayland = super.qtwayland.overrideAttrs (upstream: {
|
||||
# # preConfigure = "fixQtBuiltinPaths . '*.pr?'";
|
||||
# # });
|
||||
# # qtwayland = super.qtwayland.override {
|
||||
# # inherit (self) qtbase;
|
||||
# # };
|
||||
# # qtbase = super.qtbase.override {
|
||||
# # # fixes: "You need to set QT_HOST_PATH to cross compile Qt."
|
||||
# # inherit (emulated) stdenv;
|
||||
# # };
|
||||
# });
|
||||
qt5 = emulated.qt5.overrideScope' (self: super: {
|
||||
# emulate all the qt5 packages, but rework `libsForQt5.callPackage` and `mkDerivation`
|
||||
# to use non-emulated stdenv by default.
|
||||
mkDerivation = self.mkDerivationWith final.stdenv.mkDerivation;
|
||||
callPackage = self.newScope { inherit (self) qtCompatVersion qtModule srcs; inherit (final) stdenv; };
|
||||
});
|
||||
qt6 = prev.qt6.overrideScope' (self: super: {
|
||||
# # inherit (emulated.qt6) qtModule;
|
||||
# qtbase = super.qtbase.overrideAttrs (upstream: {
|
||||
# # cmakeFlags = upstream.cmakeFlags ++ lib.optionals (final.stdenv.buildPlatform != final.stdenv.hostPlatform) [
|
||||
# cmakeFlags = upstream.cmakeFlags ++ lib.optionals (final.stdenv.buildPlatform != final.stdenv.hostPlatform) [
|
||||
# # "-DCMAKE_CROSSCOMPILING=True" # fails to solve QT_HOST_PATH error
|
||||
# "-DQT_HOST_PATH=${final.buildPackages.qt6.full}"
|
||||
# ];
|
||||
# });
|
||||
# qtModule = args: (super.qtModule args).overrideAttrs (upstream: {
|
||||
# # the nixpkgs comment about libexec seems to be outdated:
|
||||
# # it's just that cross-compiled syncqt.pl doesn't get its #!/usr/bin/env shebang replaced.
|
||||
# preConfigure = lib.replaceStrings
|
||||
# ["${lib.getDev self.qtbase}/libexec/syncqt.pl"]
|
||||
# ["perl ${lib.getDev self.qtbase}/libexec/syncqt.pl"]
|
||||
# upstream.preConfigure;
|
||||
# });
|
||||
# # qtwayland = super.qtwayland.overrideAttrs (upstream: {
|
||||
# # preConfigure = "fixQtBuiltinPaths . '*.pr?'";
|
||||
# # });
|
||||
# # qtwayland = super.qtwayland.override {
|
||||
# # inherit (self) qtbase;
|
||||
# # };
|
||||
# # qtbase = super.qtbase.override {
|
||||
# # # fixes: "You need to set QT_HOST_PATH to cross compile Qt."
|
||||
# # inherit (emulated) stdenv;
|
||||
# # };
|
||||
|
||||
qtwebengine = super.qtwebengine.overrideAttrs (upstream: {
|
||||
# depsBuildBuild = upstream.depsBuildBuild or [] ++ [ final.pkg-config ];
|
||||
# XXX: qt seems to use its own terminology for "host" and "target":
|
||||
# - <https://www.qt.io/blog/qt6-development-hosts-and-targets>
|
||||
# - "host" = machine invoking the compiler
|
||||
# - "target" = machine on which the resulting qtwebengine.so binaries will run
|
||||
# XXX: NIX_CFLAGS_COMPILE_<machine> is how we get the `-isystem <dir>` flags.
|
||||
# probably we shouldn't blindly copy these from host machine to build machine,
|
||||
# as the headers could reasonably make different assumptions.
|
||||
preConfigure = upstream.preConfigure + ''
|
||||
# export PKG_CONFIG_HOST="$PKG_CONFIG"
|
||||
export PKG_CONFIG_HOST="$PKG_CONFIG_FOR_BUILD"
|
||||
# expose -isystem <zlib> to x86 builds
|
||||
export NIX_CFLAGS_COMPILE_x86_64_unknown_linux_gnu="$NIX_CFLAGS_COMPILE"
|
||||
export NIX_LDFLAGS_x86_64_unknown_linux_gnu="-L${final.buildPackages.zlib}/lib"
|
||||
'';
|
||||
patches = upstream.patches or [] ++ [
|
||||
# ./qtwebengine-host-pkg-config.patch
|
||||
# alternatively, look at dlopenBuildInputs
|
||||
./qtwebengine-host-cc.patch
|
||||
];
|
||||
# patch the qt pkg-config script to show us more debug info
|
||||
postPatch = upstream.postPatch or "" + ''
|
||||
sed -i s/options.debug/True/g src/3rdparty/chromium/build/config/linux/pkg-config.py
|
||||
'';
|
||||
nativeBuildInputs = upstream.nativeBuildInputs ++ [
|
||||
final.bintools-unwrapped # for readelf
|
||||
final.buildPackages.cups # for cups-config
|
||||
final.buildPackages.fontconfig
|
||||
final.buildPackages.glib
|
||||
final.buildPackages.harfbuzz
|
||||
final.buildPackages.icu
|
||||
final.buildPackages.libjpeg
|
||||
final.buildPackages.libpng
|
||||
final.buildPackages.libwebp
|
||||
final.buildPackages.nss
|
||||
# final.gcc-unwrapped.libgcc # for libgcc_s.so
|
||||
final.buildPackages.zlib
|
||||
];
|
||||
depsBuildBuild = upstream.depsBuildBuild or [] ++ [ final.pkg-config ];
|
||||
# buildInputs = upstream.buildInputs ++ [
|
||||
# final.gcc-unwrapped.libgcc # for libgcc_s.so. this gets loaded during build, suggesting i surely messed something up
|
||||
# ];
|
||||
# buildInputs = upstream.buildInputs ++ [
|
||||
# final.gcc-unwrapped.libgcc
|
||||
# ];
|
||||
# nativeBuildInputs = upstream.nativeBuildInputs ++ [
|
||||
# final.icu
|
||||
# ];
|
||||
# buildInputs = upstream.buildInputs ++ [
|
||||
# final.icu
|
||||
# ];
|
||||
# env.NIX_DEBUG="1";
|
||||
# env.NIX_DEBUG="7";
|
||||
# cmakeFlags = lib.remove "-DQT_FEATURE_webengine_system_icu=ON" upstream.cmakeFlags;
|
||||
cmakeFlags = upstream.cmakeFlags ++ lib.optionals (final.stdenv.hostPlatform != final.stdenv.buildPlatform) [
|
||||
# "--host-cc=${final.buildPackages.stdenv.cc}/bin/cc"
|
||||
# "--host-cxx=${final.buildPackages.stdenv.cc}/bin/c++"
|
||||
# these are my own vars, used by my own patch
|
||||
"-DCMAKE_HOST_C_COMPILER=${final.buildPackages.stdenv.cc}/bin/gcc"
|
||||
"-DCMAKE_HOST_CXX_COMPILER=${final.buildPackages.stdenv.cc}/bin/g++"
|
||||
"-DCMAKE_HOST_AR=${final.buildPackages.stdenv.cc}/bin/ar"
|
||||
"-DCMAKE_HOST_NM=${final.buildPackages.stdenv.cc}/bin/nm"
|
||||
];
|
||||
});
|
||||
});
|
||||
|
||||
rmlint = prev.rmlint.override {
|
||||
# fixes "Checking whether the C compiler works... no"
|
||||
@@ -1134,7 +1229,14 @@ in {
|
||||
addNativeInputs [ final.wayland-scanner ] (
|
||||
mvToNativeInputs [ final.gettext final.glib ] prev.xdg-desktop-portal-gnome
|
||||
)
|
||||
);
|
||||
).override {
|
||||
# fixes -msse2, -mfpmath=sse flags
|
||||
wrapGAppsHook4 = final.wrapGAppsHook;
|
||||
};
|
||||
# "fatal error: urcu.h: No such file or directory"
|
||||
# xfsprogs wants to compile things for the build target (BUILD_CC)
|
||||
# xfsprogs = useEmulatedStdenv prev.xfsprogs;
|
||||
xfsprogs = addNativeInputs [ final.liburcu ] prev.xfsprogs;
|
||||
# webkitgtk = prev.webkitgtk.override { stdenv = final.ccacheStdenv; };
|
||||
# webp-pixbuf-loader = prev.webp-pixbuf-loader.override {
|
||||
# # fixes "Builder called die: Cannot wrap '/nix/store/kpp8qhzdjqgvw73llka5gpnsj0l4jlg8-gdk-pixbuf-aarch64-unknown-linux-gnu-2.42.10/bin/gdk-pixbuf-thumbnailer' because it is not an executable file"
|
||||
@@ -1150,6 +1252,10 @@ in {
|
||||
});
|
||||
# XXX: aarch64 webp-pixbuf-loader wanted by gdk-pixbuf-loaders.cache.drv, wanted by aarch64 gnome-control-center
|
||||
|
||||
# "extract-binary-wrapper-cmd: line 2: strings: command not found"
|
||||
# XXX: technically this belongs in pkgs/build-support/setup-hooks/make-binary-wrapper/default.nix ?
|
||||
wrapFirefox = browser: args: addNativeInputs [ final.bintools-unwrapped ] (prev.wrapFirefox browser args);
|
||||
|
||||
wvkbd = (
|
||||
# "wayland-scanner: no such program"
|
||||
mvToNativeInputs [ final.wayland-scanner ] prev.wvkbd
|
||||
|
35
overlays/qtwebengine-host-cc.patch
Normal file
35
overlays/qtwebengine-host-cc.patch
Normal file
@@ -0,0 +1,35 @@
|
||||
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
|
||||
index 771446ece..c20da0d56 100644
|
||||
--- a/src/CMakeLists.txt
|
||||
+++ b/src/CMakeLists.txt
|
||||
@@ -172,7 +172,11 @@ if(CMAKE_CROSSCOMPILING AND NOT IOS AND NOT MACOS)
|
||||
CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=${QT_HOST_PATH}/lib/cmake/Qt6/qt.toolchain.cmake
|
||||
-DWEBENGINE_ROOT_BUILD_DIR=${PROJECT_BINARY_DIR}
|
||||
-DWEBENGINE_ROOT_SOURCE_DIR=${WEBENGINE_ROOT_SOURCE_DIR}
|
||||
- -DGN_TARGET_CPU=${TEST_architecture_arch}
|
||||
+ -DGN_TARGET_CPU=${CMAKE_HOST_SYSTEM_PROCESSOR}
|
||||
+ -DCMAKE_C_COMPILER=${CMAKE_HOST_C_COMPILER}
|
||||
+ -DCMAKE_CXX_COMPILER=${CMAKE_HOST_CXX_COMPILER}
|
||||
+ -DCMAKE_AR=${CMAKE_HOST_AR}
|
||||
+ -DCMAKE_NM=${CMAKE_HOST_NM}
|
||||
-DCMAKE_C_FLAGS=
|
||||
-DCMAKE_CXX_FLAGS=
|
||||
-DQT_FEATURE_qtwebengine_build=${QT_FEATURE_qtwebengine_build}
|
||||
diff --git a/src/host/CMakeLists.txt b/src/host/CMakeLists.txt
|
||||
index 2b92ebe85..e2ff58b35 100644
|
||||
--- a/src/host/CMakeLists.txt
|
||||
+++ b/src/host/CMakeLists.txt
|
||||
@@ -22,11 +22,11 @@ project(QtWebEngineConfigure
|
||||
find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core)
|
||||
|
||||
set(buildDir ${CMAKE_CURRENT_BINARY_DIR})
|
||||
-configure_gn_toolchain(host ${TEST_architecture_arch} ${TEST_architecture_arch}
|
||||
+configure_gn_toolchain(host ${CMAKE_HOST_SYSTEM_PROCESSOR} ${CMAKE_HOST_SYSTEM_PROCESSOR}
|
||||
${WEBENGINE_ROOT_SOURCE_DIR}/src/host/BUILD.toolchain.gn.in
|
||||
${buildDir}/host_toolchain
|
||||
)
|
||||
-get_v8_arch(GN_V8_HOST_CPU ${GN_TARGET_CPU} ${TEST_architecture_arch})
|
||||
+get_v8_arch(GN_V8_HOST_CPU ${GN_TARGET_CPU} ${CMAKE_HOST_SYSTEM_PROCESSOR})
|
||||
configure_gn_toolchain(v8 ${GN_V8_HOST_CPU} ${GN_TARGET_CPU}
|
||||
${WEBENGINE_ROOT_SOURCE_DIR}/src/host/BUILD.toolchain.gn.in
|
||||
${buildDir}/v8_toolchain)
|
14
overlays/qtwebengine-host-pkg-config.patch
Normal file
14
overlays/qtwebengine-host-pkg-config.patch
Normal file
@@ -0,0 +1,14 @@
|
||||
diff --git a/cmake/Functions.cmake b/cmake/Functions.cmake
|
||||
index 03d19992f..5ce54ca9d 100644
|
||||
--- a/cmake/Functions.cmake
|
||||
+++ b/cmake/Functions.cmake
|
||||
@@ -720,9 +720,6 @@ endfunction()
|
||||
function(create_pkg_config_wrapper wrapperName wrapperCmd)
|
||||
file(WRITE ${wrapperName}
|
||||
"#!/bin/sh\n"
|
||||
- "unset PKG_CONFIG_LIBDIR\n"
|
||||
- "unset PKG_CONFIG_PATH\n"
|
||||
- "unset PKG_CONFIG_SYSROOT_DIR\n"
|
||||
"exec ${wrapperCmd} \"$@\""
|
||||
)
|
||||
file(CHMOD ${wrapperName} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
|
24
pkgs/additional/alsa-ucm-conf-sane/default.nix
Normal file
24
pkgs/additional/alsa-ucm-conf-sane/default.nix
Normal file
@@ -0,0 +1,24 @@
|
||||
{ alsa-ucm-conf }:
|
||||
alsa-ucm-conf.overrideAttrs (upstream: {
|
||||
# upstream alsa ships with PinePhone audio configs, but they don't actually produce sound.
|
||||
# see: <https://github.com/alsa-project/alsa-ucm-conf/pull/134>
|
||||
# these audio files come from some revision of:
|
||||
# - <https://gitlab.manjaro.org/manjaro-arm/packages/community/phosh/alsa-ucm-pinephone>
|
||||
#
|
||||
# alternative to patching is to plumb `ALSA_CONFIG_UCM2 = "${./ucm2}"` environment variable into the relevant places
|
||||
# e.g. `systemd.services.pulseaudio.environment`.
|
||||
# that leaves more opportunity for gaps (i.e. missing a service),
|
||||
# on the other hand this method causes about 500 packages to be rebuilt (including qt5 and webkitgtk).
|
||||
#
|
||||
# note that with these files, the following audio device support:
|
||||
# - headphones work.
|
||||
# - "internal earpiece" works.
|
||||
# - "internal speaker" doesn't work.
|
||||
# - "analog output" doesn't work.
|
||||
postPatch = upstream.postPatch or "" + ''
|
||||
cp ${./ucm2/PinePhone}/* ucm2/Allwinner/A64/PinePhone/
|
||||
# fix the self-contained ucm files i source from to have correct path within the alsa-ucm-conf source tree
|
||||
sed -i 's:"HiFi.conf":"/Allwinner/A64/PinePhone/HiFi.conf":' ucm2/Allwinner/A64/PinePhone/PinePhone.conf
|
||||
sed -i 's:"VoiceCall.conf":"/Allwinner/A64/PinePhone/VoiceCall.conf":' ucm2/Allwinner/A64/PinePhone/PinePhone.conf
|
||||
'';
|
||||
})
|
@@ -31,8 +31,8 @@ in
|
||||
# repeat imports are deduplicated by url, even when offline.
|
||||
postBuild = ''
|
||||
makeWrapper $out/bin/gpodder $out/bin/gpodder-configured \
|
||||
--run "$out/bin/gpodder-remove-extra ~/.config/gpodderFeeds.opml" \
|
||||
--run "$out/bin/gpo import ~/.config/gpodderFeeds.opml" \
|
||||
--run "$out/bin/gpodder-remove-extra ~/.config/gpodderFeeds.opml || true" \
|
||||
--run "$out/bin/gpo import ~/.config/gpodderFeeds.opml || true" \
|
||||
|
||||
# fix up the .desktop file to invoke our wrapped application
|
||||
orig_desktop=$(readlink $out/share/applications/gpodder.desktop)
|
||||
|
@@ -0,0 +1,24 @@
|
||||
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
|
||||
index bcebe43..a15b0ef 100644
|
||||
--- a/src/CMakeLists.txt
|
||||
+++ b/src/CMakeLists.txt
|
||||
@@ -107,8 +107,8 @@ endif()
|
||||
set(RESOURCE_ROOT .)
|
||||
if(APPLE)
|
||||
set(RESOURCE_ROOT Resources)
|
||||
- add_resources(TARGET ${MAIN_TARGET} SOURCES ${CMAKE_CURRENT_BINARY_DIR}/../dist/ DEST ${RESOURCE_ROOT}/web-client/desktop)
|
||||
- add_resources(TARGET ${MAIN_TARGET} SOURCES ${CMAKE_SOURCE_DIR}/native/ DEST ${RESOURCE_ROOT}/web-client/extension)
|
||||
+ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../jellyfin-web/ DESTINATION ${RESOURCE_ROOT}/web-client/desktop)
|
||||
+ install(DIRECTORY ${CMAKE_SOURCE_DIR}/native/ DESTINATION ${RESOURCE_ROOT}/web-client/extension)
|
||||
endif()
|
||||
|
||||
if(NOT APPLE)
|
||||
@@ -121,7 +121,7 @@ if(NOT APPLE)
|
||||
install(FILES ${loc}/qtwebengine_devtools_resources.pak DESTINATION resources)
|
||||
endif()
|
||||
endforeach()
|
||||
- install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../dist/ DESTINATION ${INSTALL_RESOURCE_DIR}/web-client/desktop)
|
||||
+ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../jellyfin-web/ DESTINATION ${INSTALL_RESOURCE_DIR}/web-client/desktop)
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/native/ DESTINATION ${INSTALL_RESOURCE_DIR}/web-client/extension)
|
||||
endif()
|
||||
|
@@ -0,0 +1,13 @@
|
||||
diff --git a/src/input/InputComponent.cpp b/src/input/InputComponent.cpp
|
||||
index 0f5f129..94596b6 100644
|
||||
--- a/src/input/InputComponent.cpp
|
||||
+++ b/src/input/InputComponent.cpp
|
||||
@@ -132,7 +132,7 @@ void InputComponent::handleAction(const QString& action)
|
||||
else
|
||||
{
|
||||
qDebug() << "Invoking slot" << qPrintable(recvSlot->m_slot.data());
|
||||
- QGenericArgument arg0 = QGenericArgument();
|
||||
+ QMetaMethodArgument arg0;
|
||||
|
||||
if (recvSlot->m_hasArguments)
|
||||
arg0 = Q_ARG(const QString&, hostArguments);
|
@@ -0,0 +1,14 @@
|
||||
diff --git a/CMakeModules/QtConfiguration.cmake b/CMakeModules/QtConfiguration.cmake
|
||||
index d74a484..fb678ad 100644
|
||||
--- a/CMakeModules/QtConfiguration.cmake
|
||||
+++ b/CMakeModules/QtConfiguration.cmake
|
||||
@@ -53,8 +53,7 @@ foreach(COMP ${components})
|
||||
find_package(Qt6 REQUIRED COMPONENTS Gui)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Quick)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets)
|
||||
- find_package(Qt6 REQUIRED COMPONENTS WebEngineQuick)
|
||||
- find_package(Qt6 REQUIRED COMPONENTS WebEngineCore)
|
||||
+ find_package(Qt6 REQUIRED COMPONENTS WebEngine)
|
||||
find_package(Qt6 REQUIRED COMPONENTS OpenGL)
|
||||
find_package(Qt6 REQUIRED COMPONENTS DBus)
|
||||
|
69
pkgs/additional/jellyfin-media-player-qt6/default.nix
Normal file
69
pkgs/additional/jellyfin-media-player-qt6/default.nix
Normal file
@@ -0,0 +1,69 @@
|
||||
{ lib
|
||||
, buildPackages
|
||||
, cmake
|
||||
, fetchFromGitHub
|
||||
, jellyfin-media-player
|
||||
, libGL
|
||||
, libX11
|
||||
, libXrandr
|
||||
, libvdpau
|
||||
, mpv
|
||||
, ninja
|
||||
, pkg-config
|
||||
, python3
|
||||
, qt6
|
||||
, SDL2
|
||||
, stdenv
|
||||
}:
|
||||
(jellyfin-media-player.overrideAttrs (upstream: {
|
||||
src = fetchFromGitHub {
|
||||
owner = "jellyfin";
|
||||
repo = "jellyfin-media-player";
|
||||
rev = "qt6";
|
||||
hash = "sha256-saR/P2daqjF0G8N7BX6Rtsb1dWGjdf5MPDx1lhoioEw=";
|
||||
};
|
||||
# nixos ships two patches:
|
||||
# - the first fixes "web paths" and has *mostly* been upstreamed (so skip and manually tweak a bit)
|
||||
# - the second disables auto-update notifications (keep)
|
||||
patches = (builtins.tail upstream.patches) ++ [
|
||||
./0001-fix-web-path.patch
|
||||
./0002-qt6-build-fixes.patch
|
||||
# ./0003-qt6-components-webengine.patch
|
||||
];
|
||||
buildInputs = [
|
||||
SDL2
|
||||
libGL
|
||||
libX11
|
||||
libXrandr
|
||||
libvdpau
|
||||
mpv
|
||||
qt6.qtbase
|
||||
qt6.qtwebchannel
|
||||
qt6.qtwebengine
|
||||
# qtx11extras
|
||||
qt6.qt5compat #< new
|
||||
] ++ lib.optionals stdenv.isLinux [
|
||||
qt6.qtwayland
|
||||
];
|
||||
|
||||
nativeBuildInputs = [
|
||||
cmake
|
||||
ninja
|
||||
pkg-config
|
||||
python3
|
||||
|
||||
qt6.wrapQtAppsHook #< new: libsForQt5.callPackage implicitly adds the qt5 wrapQtAppsHook
|
||||
];
|
||||
|
||||
cmakeFlags = [
|
||||
"-DCMAKE_BUILD_TYPE=Release"
|
||||
"-DQTROOT=${qt6.qtbase}"
|
||||
"-GNinja"
|
||||
# "-DQT_DEBUG_FIND_PACKAGE=ON"
|
||||
# "--debug-find-pkg=Qt6WebEngineQuick"
|
||||
];
|
||||
|
||||
meta = upstream.meta // {
|
||||
platforms = upstream.meta.platforms ++ [ "aarch64-linux" ];
|
||||
};
|
||||
}))
|
@@ -1,109 +1,13 @@
|
||||
{ lib
|
||||
, pkgs
|
||||
, resholve
|
||||
, static-nix-shell
|
||||
, symlinkJoin
|
||||
}:
|
||||
|
||||
let
|
||||
shell-scripts = resholve.mkDerivation {
|
||||
# resholve documentation:
|
||||
# - nix: https://github.com/nixos/nixpkgs/blob/master/pkgs/development/misc/resholve/README.md
|
||||
# - generic: https://github.com/abathur/resholve
|
||||
pname = "sane-scripts";
|
||||
version = "0.1.0";
|
||||
|
||||
src = ./src;
|
||||
|
||||
solutions = {
|
||||
default = {
|
||||
# note: `scripts` refers to the store path here
|
||||
scripts = [ "bin/*" ];
|
||||
interpreter = "${pkgs.bash}/bin/bash";
|
||||
inputs = with pkgs; [
|
||||
# string is interpreted as relative path from @OUT@.
|
||||
# this lets our scripts reference eachother.
|
||||
# see: <https://github.com/abathur/resholve/issues/26>
|
||||
"bin"
|
||||
coreutils-full
|
||||
curl
|
||||
file
|
||||
findutils
|
||||
git
|
||||
gnugrep
|
||||
gnused
|
||||
gocryptfs
|
||||
ifuse
|
||||
inetutils
|
||||
inotify-tools
|
||||
iwd
|
||||
jq
|
||||
ncurses
|
||||
oath-toolkit
|
||||
openssh
|
||||
openssl
|
||||
rmlint
|
||||
rsync
|
||||
ssh-to-age
|
||||
sops
|
||||
sudo
|
||||
systemd
|
||||
util-linux
|
||||
which
|
||||
];
|
||||
keep = {
|
||||
# we write here: keep it
|
||||
"/tmp/rmlint.sh" = true;
|
||||
# intentionally escapes (into user code)
|
||||
"$external_cmd" = true;
|
||||
"$maybe_sudo" = true;
|
||||
};
|
||||
fake = {
|
||||
external = [
|
||||
# https://github.com/abathur/resholve/issues/29
|
||||
# "umount"
|
||||
# "/run/wrappers/bin/sudo"
|
||||
"sudo"
|
||||
];
|
||||
};
|
||||
fix = {
|
||||
# this replaces umount with the non-setuid-wrapper umount.
|
||||
# not sure if/where that lack of suid causes problems.
|
||||
umount = true;
|
||||
};
|
||||
prologue = "${./resholve-prologue}";
|
||||
|
||||
# list of programs which *can* or *cannot* exec their arguments
|
||||
execer = with pkgs; [
|
||||
"cannot:${git}/bin/git"
|
||||
"cannot:${gocryptfs}/bin/gocryptfs"
|
||||
"cannot:${ifuse}/bin/ifuse"
|
||||
"cannot:${iwd}/bin/iwctl"
|
||||
"cannot:${oath-toolkit}/bin/oathtool"
|
||||
"cannot:${openssh}/bin/ssh-keygen"
|
||||
"cannot:${rmlint}/bin/rmlint"
|
||||
"cannot:${rsync}/bin/rsync"
|
||||
"cannot:${sops}/bin/sops"
|
||||
"cannot:${ssh-to-age}/bin/ssh-to-age"
|
||||
"cannot:${systemd}/bin/systemctl"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
# remove python scripts (we package them further below)
|
||||
patchPhase = builtins.concatStringsSep
|
||||
"\n"
|
||||
(lib.mapAttrsToList (name: pkg: "rm ${pkg.pname}") py-scripts)
|
||||
;
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
cp -R * $out/bin/
|
||||
'';
|
||||
};
|
||||
|
||||
py-scripts = {
|
||||
nix-shell-scripts = {
|
||||
# anything added to this attrset gets symlink-joined into `sane-scripts`
|
||||
# and is made available through `sane-scripts.passthru`
|
||||
backup-ls = static-nix-shell.mkBash {
|
||||
pname = "sane-backup-ls";
|
||||
src = ./src;
|
||||
@@ -134,25 +38,178 @@ let
|
||||
src = ./src;
|
||||
pkgs = [ "transmission" ];
|
||||
};
|
||||
date-math = static-nix-shell.mkPython3Bin {
|
||||
pname = "sane-date-math";
|
||||
deadlines = static-nix-shell.mkBash {
|
||||
pname = "sane-deadlines";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" ];
|
||||
};
|
||||
dev-cargo-loop = static-nix-shell.mkBash {
|
||||
pname = "sane-dev-cargo-loop";
|
||||
src = ./src;
|
||||
pkgs = [ "inotify-tools" "ncurses" ];
|
||||
};
|
||||
find-dotfiles = static-nix-shell.mkBash {
|
||||
pname = "sane-find-dotfiles";
|
||||
src = ./src;
|
||||
pkgs = [ "findutils" ];
|
||||
};
|
||||
git-init = static-nix-shell.mkBash {
|
||||
pname = "sane-git-init";
|
||||
src = ./src;
|
||||
pkgs = [ "git" ];
|
||||
};
|
||||
ip-check = static-nix-shell.mkBash {
|
||||
pname = "sane-ip-check";
|
||||
src = ./src;
|
||||
pkgs = [ "curl" "gnugrep" ];
|
||||
};
|
||||
ip-check-upnp = static-nix-shell.mkPython3Bin {
|
||||
pname = "sane-ip-check-upnp";
|
||||
src = ./src;
|
||||
pkgs = [ "miniupnpc" ];
|
||||
postInstall = ''
|
||||
mkdir -p $out/bin/lib
|
||||
cp -R lib/* $out/bin/lib/
|
||||
'';
|
||||
};
|
||||
ip-port-forward = static-nix-shell.mkPython3Bin {
|
||||
pname = "sane-ip-port-forward";
|
||||
src = ./src;
|
||||
pkgs = [ "inetutils" "miniupnpc" ];
|
||||
postInstall = ''
|
||||
mkdir -p $out/bin/lib
|
||||
cp -R lib/* $out/bin/lib/
|
||||
'';
|
||||
};
|
||||
ip-reconnect = static-nix-shell.mkPython3Bin {
|
||||
pname = "sane-ip-reconnect";
|
||||
src = ./src;
|
||||
};
|
||||
mount-servo = static-nix-shell.mkBash {
|
||||
pname = "sane-mount-servo";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" ];
|
||||
};
|
||||
mount-servo-root = static-nix-shell.mkBash {
|
||||
pname = "sane-mount-servo-root";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" ];
|
||||
};
|
||||
private-change-passwd = static-nix-shell.mkBash {
|
||||
pname = "sane-private-change-passwd";
|
||||
src = ./src;
|
||||
pkgs = [ "gocryptfs" "rsync" "sane-scripts.private-unlock" ];
|
||||
};
|
||||
private-do = static-nix-shell.mkBash {
|
||||
pname = "sane-private-do";
|
||||
src = ./src;
|
||||
pkgs = [ "sane-scripts.private-unlock" ];
|
||||
};
|
||||
private-init = static-nix-shell.mkBash {
|
||||
pname = "sane-private-init";
|
||||
src = ./src;
|
||||
pkgs = [ "gocryptfs" ];
|
||||
};
|
||||
private-lock = static-nix-shell.mkBash {
|
||||
pname = "sane-private-lock";
|
||||
src = ./src;
|
||||
};
|
||||
private-unlock = static-nix-shell.mkBash {
|
||||
pname = "sane-private-unlock";
|
||||
src = ./src;
|
||||
pkgs = [ "gocryptfs" ];
|
||||
};
|
||||
rcp = static-nix-shell.mkBash {
|
||||
pname = "sane-rcp";
|
||||
src = ./src;
|
||||
pkgs = [ "rsync" ];
|
||||
};
|
||||
reboot = static-nix-shell.mkBash {
|
||||
pname = "sane-reboot";
|
||||
src = ./src;
|
||||
pkgs = [ "systemd" ];
|
||||
};
|
||||
reclaim-boot-space = static-nix-shell.mkPython3Bin {
|
||||
pname = "sane-reclaim-boot-space";
|
||||
src = ./src;
|
||||
};
|
||||
ip-reconnect = static-nix-shell.mkPython3Bin {
|
||||
pname = "sane-ip-reconnect";
|
||||
reclaim-disk-space = static-nix-shell.mkBash {
|
||||
pname = "sane-reclaim-disk-space";
|
||||
src = ./src;
|
||||
pkgs = [ "nix" "rmlint" "util-linux" ];
|
||||
};
|
||||
secrets-dump = static-nix-shell.mkBash {
|
||||
pname = "sane-secrets-dump";
|
||||
src = ./src;
|
||||
pkgs = [ "gnugrep" "sops" "oath-toolkit" ];
|
||||
};
|
||||
secrets-unlock = static-nix-shell.mkBash {
|
||||
pname = "sane-secrets-unlock";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" "openssh" "ssh-to-age" ];
|
||||
};
|
||||
secrets-update-keys = static-nix-shell.mkBash {
|
||||
pname = "sane-secrets-update-keys";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" "findutils" "sops" ];
|
||||
};
|
||||
shutdown = static-nix-shell.mkBash {
|
||||
pname = "sane-shutdown";
|
||||
src = ./src;
|
||||
pkgs = [ "inetutils" "systemd" ];
|
||||
};
|
||||
ssl-dump = static-nix-shell.mkBash {
|
||||
pname = "sane-ssl-dump";
|
||||
src = ./src;
|
||||
pkgs = [ "openssl" ];
|
||||
};
|
||||
stop-all-servo = static-nix-shell.mkBash {
|
||||
pname = "sane-stop-all-servo";
|
||||
src = ./src;
|
||||
pkgs = [ "systemd" ];
|
||||
};
|
||||
sudo-redirect = static-nix-shell.mkBash {
|
||||
pname = "sane-sudo-redirect";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" ];
|
||||
};
|
||||
sync-from-iphone = static-nix-shell.mkZsh {
|
||||
pname = "sane-sync-from-iphone";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" "ifuse" "rsync" ];
|
||||
};
|
||||
sync-from-servo = static-nix-shell.mkBash {
|
||||
pname = "sane-sync-from-servo";
|
||||
src = ./src;
|
||||
pkgs = [ "rsync" "sane-scripts.mount-servo" ];
|
||||
};
|
||||
vpn-down = static-nix-shell.mkBash {
|
||||
pname = "sane-vpn-down";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" "gnugrep" "gnused" "sane-scripts.ip-check" "systemd" ];
|
||||
};
|
||||
vpn-up = static-nix-shell.mkBash {
|
||||
pname = "sane-vpn-up";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" "gnugrep" "gnused" "sane-scripts.ip-check" "systemd" ];
|
||||
};
|
||||
which = static-nix-shell.mkBash {
|
||||
pname = "sane-which";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" "file" ];
|
||||
};
|
||||
wipe-browser = static-nix-shell.mkBash {
|
||||
pname = "sane-wipe-browser";
|
||||
src = ./src;
|
||||
};
|
||||
};
|
||||
in
|
||||
symlinkJoin {
|
||||
name = "sane-scripts";
|
||||
paths = [ shell-scripts ] ++ lib.attrValues py-scripts;
|
||||
paths = lib.attrValues nix-shell-scripts;
|
||||
passthru = nix-shell-scripts;
|
||||
meta = {
|
||||
description = "collection of scripts associated with uninsane systems";
|
||||
description = "collection of scripts associated with sane systems";
|
||||
homepage = "https://git.uninsane.org";
|
||||
platforms = lib.platforms.all;
|
||||
};
|
||||
|
116
pkgs/additional/sane-scripts/src/lib/sane_ssdp.py
Normal file
116
pkgs/additional/sane-scripts/src/lib/sane_ssdp.py
Normal file
@@ -0,0 +1,116 @@
|
||||
# based on this minimal SSDP client: <https://gist.github.com/schlamar/2428250>
|
||||
|
||||
import logging
|
||||
import socket
|
||||
import struct
|
||||
import subprocess
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
MCAST_GRP = "239.255.255.250"
|
||||
|
||||
class SsdpResponse:
|
||||
def __init__(self, headers: "Dict[str, str]"):
|
||||
self.headers = headers
|
||||
|
||||
@staticmethod
|
||||
def parse(msg: str) -> "Self":
|
||||
headers = {}
|
||||
for line in [m.strip() for m in msg.split("\r\n") if m.strip()]:
|
||||
if ":" not in line: continue
|
||||
sep_idx = line.find(":")
|
||||
header, content = line[:sep_idx].strip(), line[sep_idx+1:].strip()
|
||||
headers[header.upper()] = content
|
||||
if headers:
|
||||
return SsdpResponse(headers)
|
||||
|
||||
def is_rootdevice(self) -> bool:
|
||||
return self.headers.get("NT", "").lower() == "upnp:rootdevice"
|
||||
|
||||
def location(self) -> str:
|
||||
return self.headers.get("LOCATION")
|
||||
|
||||
|
||||
def get_root_devices():
|
||||
listener = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
||||
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
listener.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
|
||||
|
||||
listener.bind(("", 1900))
|
||||
logger.info("bound")
|
||||
|
||||
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
|
||||
listener.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
|
||||
|
||||
root_descs = set()
|
||||
while True:
|
||||
packet, (host, src_port) = listener.recvfrom(2048)
|
||||
logger.info(f"message from {host}")
|
||||
# if host.endswith(".1"): # router
|
||||
try:
|
||||
msg = packet.decode("utf-8")
|
||||
except:
|
||||
logger.debug("failed to decode packet to string")
|
||||
else:
|
||||
logger.debug(msg)
|
||||
resp = SsdpResponse.parse(msg)
|
||||
if resp and resp.is_rootdevice():
|
||||
root_desc = resp.location()
|
||||
if root_desc and root_desc not in root_descs:
|
||||
root_descs.add(root_desc)
|
||||
logger.info(f"root desc: {root_desc}")
|
||||
yield root_desc
|
||||
|
||||
def get_ips_from_location(location: str):
|
||||
"""
|
||||
location = URI from the Location header, e.g. http://10.78.79.1:2189/rootDesc.xml
|
||||
returns (lan, wan)
|
||||
"""
|
||||
|
||||
# get connection [s]tatus
|
||||
cmd = ["upnpc", "-u", location, "-s"]
|
||||
res = subprocess.run(cmd, capture_output=True)
|
||||
if res.returncode != 0:
|
||||
logger.info(f"get_wan_from_location failed: {cmd!r}\n{res.stderr}")
|
||||
return None
|
||||
|
||||
status = res.stdout.decode("utf-8")
|
||||
logger.debug(f"got status: {status}")
|
||||
|
||||
lan = None
|
||||
wan = None
|
||||
for line in [l.strip() for l in status.split("\n")]:
|
||||
wan_sentinel = "ExternalIPAddress ="
|
||||
lan_sentinel = "Local LAN ip address :"
|
||||
if line.startswith(wan_sentinel):
|
||||
wan = line[len(wan_sentinel):].strip()
|
||||
logger.info(f"got WAN = {wan} from {location}")
|
||||
if line.startswith(lan_sentinel):
|
||||
lan = line[len(lan_sentinel):].strip()
|
||||
logger.info(f"got LAN = {lan} from {location}")
|
||||
return lan, wan
|
||||
|
||||
def get_any_wan():
|
||||
""" return (location, LAN IP, WAN IP) for the first device seen which has a WAN IP """
|
||||
for location in get_root_devices():
|
||||
lan, wan = get_ips_from_location(location)
|
||||
if lan and wan:
|
||||
return location, lan, wan
|
||||
|
||||
def forward_port(root_device: str, proto: str, port: int, lan_ip: str, reason: str = "", duration: int = 86400):
|
||||
args = [
|
||||
"upnpc",
|
||||
"-u", root_device,
|
||||
"-e", reason,
|
||||
"-a", lan_ip,
|
||||
str(port),
|
||||
str(port),
|
||||
proto,
|
||||
str(duration),
|
||||
]
|
||||
|
||||
logger.debug(f"running: {args!r}")
|
||||
stdout = subprocess.check_output(args).decode("utf-8")
|
||||
|
||||
logger.info(stdout)
|
@@ -10,6 +10,7 @@ returns select results and magnet links
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import json
|
||||
import natsort
|
||||
import requests
|
||||
@@ -23,6 +24,8 @@ ENDPOINTS = dict(
|
||||
|
||||
epoch = datetime(1970, 1, 1)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def try_parse_time(t: str):
|
||||
try:
|
||||
return datetime.fromisoformat(t)
|
||||
@@ -44,10 +47,15 @@ class Torrent:
|
||||
size: int
|
||||
tracker: str
|
||||
title: str
|
||||
magnet: str
|
||||
magnet: "Optional[str]"
|
||||
http_dl_uri: "Optional[str]" # probably a .torrent file but it COULD be a referral to a magnet:// URI
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.seeders}[S]\t{self.pub_date}\t{self.mib}M\t{self.tracker}\t{self.title}\t{self.magnet}"
|
||||
return f"{self.seeders}[S]\t{self.pub_date}\t{self.mib}M\t{self.tracker}\t{self.title}\n\t{self.dl_uri}"
|
||||
|
||||
@property
|
||||
def dl_uri(self) -> str:
|
||||
return self.magnet or self.http_dl_uri
|
||||
|
||||
@property
|
||||
def mib(self) -> int:
|
||||
@@ -55,15 +63,32 @@ class Torrent:
|
||||
|
||||
@staticmethod
|
||||
def from_dict(d: dict) -> 'Torrent':
|
||||
logger.debug(f"Torrent.from_dict: fields: { ' '.join(d.keys()) }")
|
||||
for k, v in d.items():
|
||||
if k not in ("Seeders", "PublishDate", "Size", "Tracker", "Title", "MagnetUri", "Guid", "Link") and \
|
||||
v != None and v != "" and v != [] and v != {}:
|
||||
logger.debug(f" {k} = {v}")
|
||||
|
||||
seeders = d.get("Seeders")
|
||||
pub_date = d.get("PublishDate")
|
||||
size = d.get("Size")
|
||||
tracker = d.get("Tracker")
|
||||
title = d.get("Title")
|
||||
magnet = d.get("MagnetUri")
|
||||
if seeders is not None and pub_date is not None and title is not None and magnet is not None:
|
||||
magnet = d.get("MagnetUri") or d.get("Guid")
|
||||
http_dl_uri = d.get("Link")
|
||||
|
||||
if magnet and not magnet.startswith("magnet:"):
|
||||
logger.info(f"invalid magnet: {magnet}")
|
||||
magnet = None
|
||||
|
||||
# jackett returns bad DL URIs because it doesn't know its public URI
|
||||
firewalled_host = "http://10.0.1.6:9117/"
|
||||
if http_dl_uri and http_dl_uri.startswith(firewalled_host):
|
||||
http_dl_uri = SERVICE + "/" + http_dl_uri[len(firewalled_host):]
|
||||
|
||||
if seeders is not None and pub_date is not None and title is not None and (magnet is not None or http_dl_uri is not None):
|
||||
pub_date = parse_time(pub_date)
|
||||
return Torrent(seeders, pub_date, size, tracker, title, magnet)
|
||||
return Torrent(seeders, pub_date, size, tracker, title, magnet, http_dl_uri)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return dict(
|
||||
@@ -99,9 +124,11 @@ class Client:
|
||||
|
||||
def parse_args(args: list) -> dict:
|
||||
options = dict(
|
||||
top="5",
|
||||
full=False,
|
||||
query="",
|
||||
json=False,
|
||||
verbose=False,
|
||||
)
|
||||
while args:
|
||||
arg = args[0]
|
||||
@@ -119,9 +146,14 @@ def parse_args(args: list) -> dict:
|
||||
return options
|
||||
|
||||
def main(args: list):
|
||||
logging.basicConfig()
|
||||
options = parse_args(args)
|
||||
|
||||
if options["verbose"]:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
query = options["query"]
|
||||
num_listings = 100 if options["full"] else 5
|
||||
num_listings = 1000 if options["full"] else int(options["top"])
|
||||
client = Client()
|
||||
res = client.query(query)
|
||||
if options["json"]:
|
||||
|
@@ -1,348 +0,0 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])"
|
||||
|
||||
# i just went overboard playing around with parsers, is all.
|
||||
# use this like `./sane-date-math 'today - 5d'`
|
||||
# of course, it handles parentheses and operator precedence/associativity, so you can do sillier things like
|
||||
# `./sane-date-math ' today - (1+3 *4 - ((0)) ) *7d '`
|
||||
|
||||
|
||||
import abc
|
||||
from datetime import datetime, timedelta
|
||||
import sys
|
||||
|
||||
class Token:
|
||||
def __init__(self, c: str):
|
||||
self.c = c
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.c!r}"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.c
|
||||
|
||||
def __eq__(self, other: 'Token') -> bool:
|
||||
return self.c == other.c
|
||||
|
||||
PLUS = Token('+')
|
||||
MINUS = Token('-')
|
||||
ASTERISK = Token('*')
|
||||
SPACE = Token(' ')
|
||||
OPEN_PAREN = Token('(')
|
||||
CLOSE_PAREN = Token(')')
|
||||
UNDERSCORE = Token('_')
|
||||
DIGITS = [Token(c) for c in '0123456789']
|
||||
ALPHA_LOWER = [Token(c) for c in 'abcdefghijklmnopqrstuvwxyz']
|
||||
ALPHA_UPPER = [Token(t.c.upper()) for t in ALPHA_LOWER]
|
||||
ALPHA = ALPHA_LOWER + ALPHA_UPPER
|
||||
ALPHA_UNDER = ALPHA + [UNDERSCORE]
|
||||
ALPHA_NUM_UNDER = ALPHA_UNDER + DIGITS
|
||||
|
||||
class ParserContext:
|
||||
def feed(self, token: Token) -> 'ParserContext':
|
||||
return None # can't ingest the token
|
||||
|
||||
def upgrade(self) -> 'ParserContext':
|
||||
return None # no upgrade path
|
||||
|
||||
class Parser:
|
||||
"""
|
||||
LR parser.
|
||||
keeps exactly one root item, and for each input token
|
||||
feeds it to the root, possibly "upgrading" the root N times
|
||||
before it's able to be fed.
|
||||
"""
|
||||
def __init__(self, root: ParserContext):
|
||||
self.root = root
|
||||
|
||||
def feed(self, token: Token) -> bool:
|
||||
new_root = self.root.feed(token)
|
||||
if new_root is not None:
|
||||
self.root = new_root
|
||||
return True
|
||||
else:
|
||||
# root can't directly accept this item.
|
||||
# "upgrade" it and try again.
|
||||
new_root = self.root.upgrade()
|
||||
if new_root is None: return False
|
||||
self.root = new_root
|
||||
return self.feed(token)
|
||||
|
||||
def complete(self) -> ParserContext:
|
||||
# upgrade the root as far as possible before returning
|
||||
root = None
|
||||
new_root = self.root
|
||||
while new_root is not None:
|
||||
root = new_root
|
||||
new_root = root.upgrade()
|
||||
|
||||
return root
|
||||
|
||||
class ReprParserContext(ParserContext):
|
||||
""" helper that gives a good default repr to most contexts """
|
||||
def __init__(self, items: list = None):
|
||||
self.items = items if items is not None else []
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'{self.__class__.__name__}({self.items!r})'
|
||||
|
||||
|
||||
class BaseContext(ReprParserContext):
|
||||
""" empty context; initial state of the parser """
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
if token == SPACE:
|
||||
return self
|
||||
if token == OPEN_PAREN:
|
||||
return ParenContext(BaseContext())
|
||||
if token in DIGITS:
|
||||
return IntegerContext([token])
|
||||
if token in ALPHA_UNDER:
|
||||
return IdentifierContext([token])
|
||||
|
||||
class IdentifierContext(ReprParserContext):
|
||||
""" context is an identifier like `today` """
|
||||
def __init__(self, tokens: list):
|
||||
super().__init__(tokens)
|
||||
self.tokens = tokens
|
||||
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
if token in ALPHA_NUM_UNDER:
|
||||
return IdentifierContext(self.tokens + [token])
|
||||
|
||||
def upgrade(self) -> ParserContext:
|
||||
return StrongValueContext(self)
|
||||
|
||||
class IntegerContext(ReprParserContext):
|
||||
""" context is an integer like `45` """
|
||||
def __init__(self, tokens: list):
|
||||
super().__init__(tokens)
|
||||
self.tokens = tokens
|
||||
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
if token in DIGITS:
|
||||
return IntegerContext(self.tokens + [token])
|
||||
if token == Token('d'):
|
||||
return DurationContext(self)
|
||||
|
||||
def upgrade(self) -> ParserContext:
|
||||
# can't continue the integer; it becomes a value
|
||||
return StrongValueContext(self)
|
||||
|
||||
class DurationContext(ReprParserContext):
|
||||
""" context is a duration like `14d` """
|
||||
def __init__(self, value: IntegerContext):
|
||||
super().__init__([value])
|
||||
self.value = value
|
||||
|
||||
def upgrade(self) -> ParserContext:
|
||||
return StrongValueContext(self)
|
||||
|
||||
class BaseValueContext(ReprParserContext):
|
||||
""" abstract base for types that can be used in compound expressions """
|
||||
def __init__(self, value: ParserContext):
|
||||
super().__init__([value])
|
||||
self.value = value
|
||||
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
if token == SPACE:
|
||||
return self
|
||||
|
||||
class StrongValueContext(BaseValueContext):
|
||||
"""
|
||||
in the context of operators, a strong value is something which prefers
|
||||
to not be grabbed by a lhs value.
|
||||
|
||||
so for example, strong values have the opportunity to initiate a multiply operation before the lhs closes an addition operation that this strong value is a part of
|
||||
"""
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
if token == ASTERISK:
|
||||
return BinaryOpContext(self, token, BaseContext())
|
||||
return super().feed(token)
|
||||
|
||||
def upgrade(self) -> ParserContext:
|
||||
return WeakValueContext(self.value)
|
||||
|
||||
class WeakValueContext(BaseValueContext):
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
if token == PLUS:
|
||||
return BinaryOpContext(self, token, BaseContext())
|
||||
if token == MINUS:
|
||||
return BinaryOpContext(self, token, BaseContext())
|
||||
|
||||
return super().feed(token)
|
||||
|
||||
class BinaryOpContext(ReprParserContext):
|
||||
""" context for a binary operation. the LHS and operator are parsed, but the rhs may not yet contain a value """
|
||||
def __init__(self, lhs: BaseValueContext, oper: Token, rhs: ParserContext):
|
||||
super().__init__([lhs, oper, rhs])
|
||||
self.lhs = lhs
|
||||
self.oper = oper
|
||||
self.rhs = rhs
|
||||
|
||||
@property
|
||||
def precedence_class(self) -> type:
|
||||
if self.oper in [PLUS, MINUS]:
|
||||
return WeakValueContext
|
||||
if self.oper == ASTERISK:
|
||||
return StrongValueContext
|
||||
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
new_rhs = self.rhs.feed(token)
|
||||
if new_rhs is not None:
|
||||
return BinaryOpContext(self.lhs, self.oper, new_rhs)
|
||||
|
||||
def upgrade(self) -> ParserContext:
|
||||
new_rhs = self.rhs.upgrade()
|
||||
if new_rhs is None: return None
|
||||
|
||||
# upgrade self once the rhs has reach the required precedence compatible with this operator
|
||||
new_self = BinaryOpContext(self.lhs, self.oper, new_rhs)
|
||||
if isinstance(new_rhs, self.precedence_class):
|
||||
return StrongValueContext(self) # close the operation
|
||||
|
||||
return new_self
|
||||
|
||||
class ParenContext(ReprParserContext):
|
||||
""" context for a value contained within parentheses """
|
||||
def __init__(self, inner: ParserContext):
|
||||
super().__init__([inner])
|
||||
self.inner = inner
|
||||
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
new_inner = self.inner.feed(token)
|
||||
if new_inner is not None:
|
||||
return ParenContext(new_inner)
|
||||
|
||||
if token == CLOSE_PAREN and isinstance(self.inner, WeakValueContext):
|
||||
return StrongValueContext(self)
|
||||
|
||||
def upgrade(self) -> ParserContext:
|
||||
new_inner = self.inner.upgrade()
|
||||
if new_inner is not None:
|
||||
return ParenContext(new_inner)
|
||||
|
||||
|
||||
## AstItems are produced from a ParserContext input
|
||||
## ParserContext parse outputs are translated into `AstItem`s before evaluation
|
||||
## so that we can operate on a higher-level tree that directly encodes native values like integers
|
||||
|
||||
class AstItem(metaclass=abc.ABCMeta):
|
||||
@abc.abstractmethod
|
||||
def eval(self, context: dict):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def decode_item(p: ParserContext) -> 'AstItem':
|
||||
if isinstance(p, IntegerContext):
|
||||
return Literal(AstItem.decode_integer(p))
|
||||
if isinstance(p, DurationContext):
|
||||
return Literal(timedelta(AstItem.decode_integer(p.value)))
|
||||
if isinstance(p, IdentifierContext):
|
||||
return Variable(AstItem.decode_identifier(p))
|
||||
if isinstance(p, BaseValueContext):
|
||||
return AstItem.decode_item(p.value)
|
||||
if isinstance(p, BinaryOpContext):
|
||||
return AstItem.decode_bin_op(
|
||||
p.oper.c,
|
||||
AstItem.decode_item(p.lhs),
|
||||
AstItem.decode_item(p.rhs)
|
||||
)
|
||||
if isinstance(p, ParenContext):
|
||||
return AstItem.decode_item(p.inner)
|
||||
|
||||
@staticmethod
|
||||
def decode_integer(p: IntegerContext) -> int:
|
||||
return int(''.join(t.c for t in p.tokens))
|
||||
|
||||
@staticmethod
|
||||
def decode_identifier(p: IdentifierContext) -> str:
|
||||
return ''.join(t.c for t in p.tokens)
|
||||
|
||||
@staticmethod
|
||||
def decode_bin_op(ty: str, lhs: 'AstItem', rhs: 'AstItem') -> 'BinaryOp':
|
||||
if ty == '+':
|
||||
return AddOp(lhs, rhs)
|
||||
if ty == '-':
|
||||
return SubOp(lhs, rhs)
|
||||
if ty == '*':
|
||||
return MulOp(lhs, rhs)
|
||||
|
||||
class Literal(AstItem):
|
||||
def __init__(self, v):
|
||||
self.v = v
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.v)
|
||||
|
||||
def eval(self, context: dict):
|
||||
return self.v
|
||||
|
||||
class Variable(AstItem):
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def eval(self, context: dict):
|
||||
return context[self.name]
|
||||
|
||||
class BinaryOp(AstItem):
|
||||
def __init__(self, lhs, rhs):
|
||||
self.lhs = lhs
|
||||
self.rhs = rhs
|
||||
|
||||
class AddOp(BinaryOp):
|
||||
def __str__(self):
|
||||
return f"({self.lhs} + {self.rhs})"
|
||||
|
||||
def eval(self, context: dict):
|
||||
return self.lhs.eval(context) + self.rhs.eval(context)
|
||||
|
||||
class SubOp(BinaryOp):
|
||||
def __str__(self):
|
||||
return f"({self.lhs} - {self.rhs})"
|
||||
|
||||
def eval(self, context: dict):
|
||||
return self.lhs.eval(context) - self.rhs.eval(context)
|
||||
|
||||
class MulOp(BinaryOp):
|
||||
def __str__(self):
|
||||
return f"({self.lhs} * {self.rhs})"
|
||||
|
||||
def eval(self, context: dict):
|
||||
return self.lhs.eval(context) * self.rhs.eval(context)
|
||||
|
||||
|
||||
## toplevel routine. tokenize -> parse -> decode to AST -> evaluate
|
||||
|
||||
def tokenize(stream: str) -> list:
|
||||
return [Token(char) for char in stream]
|
||||
|
||||
def parse(tokens: list) -> ParserContext:
|
||||
parser = Parser(BaseContext())
|
||||
for i, t in enumerate(tokens):
|
||||
result = parser.feed(t)
|
||||
# print(f"i={i}; t={t}; state: {ctx!r}")
|
||||
assert result, f"unexpected token '{t}' at {i}; state: {parser.complete()!r}"
|
||||
|
||||
return parser.complete()
|
||||
|
||||
|
||||
def evaluate(expr: str) -> object:
|
||||
tok = tokenize(expr)
|
||||
parse_tree = parse(tok)
|
||||
print(parse_tree)
|
||||
ast = AstItem.decode_item(parse_tree)
|
||||
print(ast)
|
||||
|
||||
env = dict(
|
||||
today=datetime.now()
|
||||
)
|
||||
return ast.eval(env)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
expr = " ".join(sys.argv[1:])
|
||||
print(evaluate(expr))
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p coreutils-full
|
||||
|
||||
# processes a tab-separated "deadlines" file and alerts for any upcoming events.
|
||||
#
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p inotify-tools -p ncurses
|
||||
# watches PWD for any changes underneath it and re-runs `cargo build --a>
|
||||
# optionally, provide your own build command as the first argument
|
||||
|
||||
|
@@ -1,9 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p findutils
|
||||
# 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 ~/ -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
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p git
|
||||
|
||||
set -x
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p curl -p gnugrep
|
||||
ip=$(curl --silent https://ipinfo.io/ip)
|
||||
echo "$ip" | grep -P " *^\d+\.\d+\.\d+\.\d+ *$"
|
||||
exit $?
|
||||
|
@@ -1,18 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# query the WAN IP address OF MY ROUTER
|
||||
# requires creds
|
||||
passwd=$(sudo cat /run/secrets/router_passwd)
|
||||
cookie=$(mktemp)
|
||||
curlflags="curl --silent --insecure --cookie-jar $cookie --connect-timeout 5"
|
||||
|
||||
# authenticate
|
||||
curl $curlflags \
|
||||
--data "username=admin&password=$passwd" \
|
||||
https://192.168.0.1
|
||||
# query the WAN IP
|
||||
ip=$(curl $curlflags \
|
||||
-H "X-Requested-With: XMLHttpRequest" \
|
||||
"https://192.168.0.1/cgi/cgi_action?Action=GetConnectionStatus" \
|
||||
| jq -r .wan_status.ipaddr)
|
||||
echo "$ip" | grep -P " *^\d+\.\d+\.\d+\.\d+ *$"
|
||||
exit $?
|
27
pkgs/additional/sane-scripts/src/sane-ip-check-upnp
Executable file
27
pkgs/additional/sane-scripts/src/sane-ip-check-upnp
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p miniupnpc
|
||||
|
||||
# best to run this with an external timeout. e.g.
|
||||
# - `timeout 60 sane-ip-check-upnp`
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
d = os.path.dirname(__file__)
|
||||
sys.path.insert(0, d)
|
||||
|
||||
from lib.sane_ssdp import get_any_wan
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.basicConfig()
|
||||
|
||||
for arg in sys.argv[1:]:
|
||||
if arg == "-v":
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
elif arg == "-vv":
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
else:
|
||||
raise RuntimeError(f"invalid CLI argument {arg!r}")
|
||||
_rootdev, _lan_ip, wan_ip = get_any_wan()
|
||||
print(wan_ip)
|
106
pkgs/additional/sane-scripts/src/sane-ip-port-forward
Executable file
106
pkgs/additional/sane-scripts/src/sane-ip-port-forward
Executable file
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p inetutils -p miniupnpc
|
||||
|
||||
'''
|
||||
USAGE: sane-ip-port-forward [options] [proto:port[:desc]]*
|
||||
|
||||
options:
|
||||
-v: verbose (show info messages)
|
||||
-vv: more verbose (show debug messages)
|
||||
-h: show this help messages
|
||||
-d <int>: lease for the given duration in seconds (default: {DEFAULT_LEASE_SEC})
|
||||
|
||||
proto:port[:desc]:
|
||||
proto is `udp` or `tcp` (case insensitive)
|
||||
port is any integer 1-65535 inclusive
|
||||
desc is some public description of the port forward (for humans trying to understand a network config)
|
||||
'''
|
||||
|
||||
DEFAULT_LEASE_SEC = 86400
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, ".")
|
||||
|
||||
from lib.sane_ssdp import get_any_wan, forward_port
|
||||
|
||||
class BadCliArgs(Exception):
|
||||
def __init__(self, msg: str = None):
|
||||
helpstr = __doc__.format(DEFAULT_LEASE_SEC=DEFAULT_LEASE_SEC).strip()
|
||||
if msg:
|
||||
super().__init__(f"{msg}\n\n{helpstr}")
|
||||
else:
|
||||
super().__init__(helpstr)
|
||||
|
||||
def try_parse_port(s: str):
|
||||
"""
|
||||
`udp:53` -> ["udp", 53, ""]
|
||||
`tcp:65535:my description` -> ["tcp", 65535, "my description"]
|
||||
"""
|
||||
s = s.strip()
|
||||
|
||||
def split_at_colon(s_: str):
|
||||
next_colon = s_.find(":")
|
||||
return s_[:next_colon], s_[next_colon+1:]
|
||||
|
||||
if ":" not in s: return # invalid format
|
||||
proto, rest = split_at_colon(s)
|
||||
|
||||
if ":" in rest:
|
||||
portstr, desc = split_at_colon(rest)
|
||||
else:
|
||||
portstr = rest
|
||||
desc = ""
|
||||
|
||||
try:
|
||||
proto, port = proto.lower(), int(portstr)
|
||||
assert proto in ["tcp", "udp"]
|
||||
assert 0 < port < 65536
|
||||
return proto, port, desc
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def parse_args(argv: "List[str]") -> "List[('udp'|'tcp', port: int, description: str)]":
|
||||
"""
|
||||
returns (list of forwards, lease duration)
|
||||
where:
|
||||
- list of forwards is [('udp'|'tcp', port: int, description: str)]
|
||||
- lease duration is seconds: int
|
||||
"""
|
||||
forwards = []
|
||||
duration = DEFAULT_LEASE_SEC
|
||||
unparsed = sys.argv[1:][::-1]
|
||||
while unparsed:
|
||||
arg = unparsed.pop()
|
||||
if arg == "-h":
|
||||
raise BadCliArgs()
|
||||
if arg == "-v":
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
elif arg == "-vv":
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
elif arg == "-d" and unparsed:
|
||||
d = unparsed.pop()
|
||||
try:
|
||||
duration = int(d)
|
||||
except Exception:
|
||||
raise BadCliArgs(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}")
|
||||
return forwards, duration
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.basicConfig()
|
||||
|
||||
try:
|
||||
forwards, duration = parse_args(sys.argv)
|
||||
except BadCliArgs as e:
|
||||
print(e)
|
||||
sys.exit(1)
|
||||
|
||||
root_device, lan, _wan = get_any_wan()
|
||||
for (proto, port, reason) in forwards:
|
||||
forward_port(root_device, proto, port, lan_ip=lan, reason=reason, duration=duration)
|
@@ -1,4 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p coreutils-full
|
||||
#^ requires `sudo` and `mount` to be installed system-wide (suid)
|
||||
|
||||
set -ex
|
||||
|
||||
# if lan not mounted, then try to mount it
|
||||
|
@@ -1,4 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p coreutils-full
|
||||
#^ requires `sudo` and `mount` to be installed system-wide (suid)
|
||||
#
|
||||
set -ex
|
||||
|
||||
# if lan not mounted, then try to mount it
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p gocryptfs -p rsync -p sane-scripts.private-unlock
|
||||
|
||||
set -ex
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p sane-scripts.private-unlock
|
||||
|
||||
# unlock the ~/private store, run some command, and then re-lock the store
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p gocryptfs
|
||||
|
||||
set -ex
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash
|
||||
|
||||
sudo umount /home/colin/private
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p gocryptfs
|
||||
|
||||
set -ex
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p rsync
|
||||
# rsync, with sane defaults
|
||||
# + verbosity
|
||||
# + default to cwd as destination if none is provided
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p systemd
|
||||
|
||||
target="$1"
|
||||
host="$(hostname)"
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p nix -p rmlint -p util-linux
|
||||
# script to reclaim some hard drive space
|
||||
# some of this is documented here:
|
||||
# - <https://nixos.wiki/wiki/Storage_optimization>
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p gnugrep -p oath-toolkit -p sops
|
||||
# use: `sane-dump-secret /path/to/accounts/website.yaml`
|
||||
# dumps relevant information about the account, include a OTP code if present
|
||||
secrets=$(sops -d --output-type dotenv $1)
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i 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)
|
||||
set -ex
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i 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)
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p inetutils -p systemd
|
||||
|
||||
target="$1"
|
||||
host="$(hostname)"
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p openssl
|
||||
|
||||
# dump info about the provided SSL certificate
|
||||
cert="$1"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user