2configs
5pkgs
machines
skills
···
189
189
190
190
# Container configurations for use with extra-container
191
191
# Auto-discovers machines with container.nix
192
192
-
flake.containerConfigurations = nixpkgs.lib.mapAttrs
193
193
-
(machineName: _: import ./machines/${machineName}/container.nix { inherit self; })
194
194
-
(
195
195
-
nixpkgs.lib.filterAttrs (
196
196
-
machineName: _: builtins.pathExists ./machines/${machineName}/container.nix
197
197
-
) (builtins.readDir ./machines)
198
198
-
);
192
192
+
flake.containerConfigurations =
193
193
+
nixpkgs.lib.mapAttrs
194
194
+
(machineName: _: import ./machines/${machineName}/container.nix { inherit self; })
195
195
+
(
196
196
+
nixpkgs.lib.filterAttrs (
197
197
+
machineName: _: builtins.pathExists ./machines/${machineName}/container.nix
198
198
+
) (builtins.readDir ./machines)
199
199
+
);
199
200
perSystem =
200
201
{
201
202
lib,
···
222
223
};
223
224
};
224
225
clan-cli = clan-core.packages.${system}.clan-cli;
225
225
-
} // lib.mapAttrs' (name: containerConfig:
226
226
-
lib.nameValuePair "container-${name}" (inputs.extra-container.lib.buildContainers {
227
227
-
inherit system;
228
228
-
nixpkgs = inputs.nixpkgs;
229
229
-
config.containers.${name} = containerConfig;
230
230
-
})
226
226
+
}
227
227
+
// lib.mapAttrs' (
228
228
+
name: containerConfig:
229
229
+
lib.nameValuePair "container-${name}" (
230
230
+
inputs.extra-container.lib.buildContainers {
231
231
+
inherit system;
232
232
+
nixpkgs = inputs.nixpkgs;
233
233
+
config.containers.${name} = containerConfig;
234
234
+
}
235
235
+
)
231
236
) self.containerConfigurations;
232
237
devShells.default = pkgs.mkShell {
233
238
packages = [
···
1
1
-
{ config, lib, pkgs, ... }:
1
1
+
{ config, pkgs, ... }:
2
2
{
3
3
# Local DNS64 resolver for containers
4
4
# Synthesizes AAAA records with 64:ff9b::/96 prefix for IPv4-only hosts
···
6
6
enable = true;
7
7
settings = {
8
8
server = {
9
9
-
interface = [ "fd00:c700::1" "127.0.0.1" ];
9
9
+
interface = [
10
10
+
"fd00:c700::1"
11
11
+
"127.0.0.1"
12
12
+
];
10
13
access-control = [
11
14
"fd00:c700::/64 allow"
12
15
"127.0.0.0/8 allow"
···
24
27
{
25
28
name = ".";
26
29
forward-addr = [
27
27
-
"1.1.1.1" # Cloudflare (IPv4 - always works)
28
28
-
"8.8.8.8" # Google (IPv4)
29
29
-
"2606:4700:4700::1111" # Cloudflare (IPv6 - when available)
30
30
+
"1.1.1.1" # Cloudflare (IPv4 - always works)
31
31
+
"8.8.8.8" # Google (IPv4)
32
32
+
"2606:4700:4700::1111" # Cloudflare (IPv6 - when available)
30
33
];
31
34
}
32
35
];
···
46
49
address = [ "fd00:c700::1/64" ];
47
50
networkConfig = {
48
51
ConfigureWithoutCarrier = true;
49
49
-
IPv6SendRA = true; # Enable Router Advertisements
52
52
+
IPv6SendRA = true; # Enable Router Advertisements
50
53
};
51
54
52
55
# Router Advertisement configuration
53
56
ipv6SendRAConfig = {
54
54
-
Managed = false; # No DHCPv6 for addresses (use SLAAC)
55
55
-
OtherInformation = false; # No DHCPv6 for DNS (use RDNSS)
57
57
+
Managed = false; # No DHCPv6 for addresses (use SLAAC)
58
58
+
OtherInformation = false; # No DHCPv6 for DNS (use RDNSS)
56
59
RouterLifetimeSec = 1800;
57
57
-
DNS = [ "fd00:c700::1" ]; # RDNSS: advertise DNS64 resolver
60
60
+
DNS = [ "fd00:c700::1" ]; # RDNSS: advertise DNS64 resolver
58
61
};
59
62
ipv6Prefixes = [
60
63
{ Prefix = "fd00:c700::/64"; }
···
91
94
# Update Jool pool4 with current IPv4 address (needed for NAT64 on dynamic IPs)
92
95
systemd.services.jool-pool4-update = {
93
96
description = "Update Jool NAT64 pool4 with current IPv4";
94
94
-
after = [ "jool.service" "network-online.target" ];
97
97
+
after = [
98
98
+
"jool.service"
99
99
+
"network-online.target"
100
100
+
];
95
101
wants = [ "network-online.target" ];
96
102
wantedBy = [ "multi-user.target" ];
97
97
-
path = [ pkgs.iproute2 pkgs.gawk pkgs.jool-cli ];
103
103
+
path = [
104
104
+
pkgs.iproute2
105
105
+
pkgs.gawk
106
106
+
pkgs.jool-cli
107
107
+
];
98
108
serviceConfig = {
99
109
Type = "oneshot";
100
110
RemainAfterExit = true;
···
120
130
# Firewall rules for container bridge
121
131
krebs.iptables.tables.filter.FORWARD.rules = [
122
132
# IPv6 forwarding for containers
123
123
-
{ v4 = false; v6 = true; predicate = "-i ctr0"; target = "ACCEPT"; }
124
124
-
{ v4 = false; v6 = true; predicate = "-o ctr0 -m conntrack --ctstate RELATED,ESTABLISHED"; target = "ACCEPT"; }
133
133
+
{
134
134
+
v4 = false;
135
135
+
v6 = true;
136
136
+
predicate = "-i ctr0";
137
137
+
target = "ACCEPT";
138
138
+
}
139
139
+
{
140
140
+
v4 = false;
141
141
+
v6 = true;
142
142
+
predicate = "-o ctr0 -m conntrack --ctstate RELATED,ESTABLISHED";
143
143
+
target = "ACCEPT";
144
144
+
}
125
145
# IPv4 forwarding for NAT64 translated packets
126
126
-
{ v4 = true; v6 = false; predicate = "-m conntrack --ctstate RELATED,ESTABLISHED"; target = "ACCEPT"; }
127
127
-
{ v4 = true; v6 = false; predicate = ""; target = "ACCEPT"; }
146
146
+
{
147
147
+
v4 = true;
148
148
+
v6 = false;
149
149
+
predicate = "-m conntrack --ctstate RELATED,ESTABLISHED";
150
150
+
target = "ACCEPT";
151
151
+
}
152
152
+
{
153
153
+
v4 = true;
154
154
+
v6 = false;
155
155
+
predicate = "";
156
156
+
target = "ACCEPT";
157
157
+
}
128
158
];
129
159
130
160
# NAT66 - masquerade container IPv6 to reach public IPv6 (when host has IPv6)
131
161
# NAT44 - masquerade NAT64 translated IPv4 packets (exclude localhost!)
132
162
krebs.iptables.tables.nat.POSTROUTING.rules = [
133
133
-
{ v4 = false; v6 = true; predicate = "-s fd00:c700::/64 ! -d fd00:c700::/64"; target = "MASQUERADE"; }
134
134
-
{ v4 = true; v6 = false; predicate = "! -d 127.0.0.0/8 ! -o lo"; target = "MASQUERADE"; } # Don't masquerade localhost
163
163
+
{
164
164
+
v4 = false;
165
165
+
v6 = true;
166
166
+
predicate = "-s fd00:c700::/64 ! -d fd00:c700::/64";
167
167
+
target = "MASQUERADE";
168
168
+
}
169
169
+
{
170
170
+
v4 = true;
171
171
+
v6 = false;
172
172
+
predicate = "! -d 127.0.0.0/8 ! -o lo";
173
173
+
target = "MASQUERADE";
174
174
+
} # Don't masquerade localhost
135
175
];
136
176
137
177
# Allow containers to reach host's DNS64 resolver (IPv6 only)
138
178
krebs.iptables.tables.filter.INPUT.rules = [
139
139
-
{ v4 = false; v6 = true; predicate = "-s fd00:c700::/64 -p udp --dport 53"; target = "ACCEPT"; }
140
140
-
{ v4 = false; v6 = true; predicate = "-s fd00:c700::/64 -p tcp --dport 53"; target = "ACCEPT"; }
179
179
+
{
180
180
+
v4 = false;
181
181
+
v6 = true;
182
182
+
predicate = "-s fd00:c700::/64 -p udp --dport 53";
183
183
+
target = "ACCEPT";
184
184
+
}
185
185
+
{
186
186
+
v4 = false;
187
187
+
v6 = true;
188
188
+
predicate = "-s fd00:c700::/64 -p tcp --dport 53";
189
189
+
target = "ACCEPT";
190
190
+
}
141
191
];
142
192
}
···
1
1
-
{ self, pkgs, ... }:
1
1
+
{ pkgs, ... }:
2
2
{
3
3
# Blacklist DVB-T driver so rtl-sdr can use the device
4
4
boot.blacklistedKernelModules = [ "dvb_usb_rtl28xxu" ];
···
8
8
services.udev.packages = [ pkgs.rtl-sdr-blog ];
9
9
10
10
# Ensure plugdev group exists and mainUser is a member (used by rtl-sdr udev rules)
11
11
-
users.groups.plugdev = {};
11
11
+
users.groups.plugdev = { };
12
12
users.users.mainUser.extraGroups = [ "plugdev" ];
13
13
14
14
# SDR and TETRA tools
15
15
environment.systemPackages = [
16
16
-
pkgs.rtl-sdr-blog # V4 compatible drivers and tools
16
16
+
pkgs.rtl-sdr-blog # V4 compatible drivers and tools
17
17
];
18
18
}
···
1
1
{
2
2
-
inputs,
3
2
lib,
4
4
-
self,
5
3
...
6
4
}:
7
5
{
···
12
10
lib.filterAttrs (_: type: type == "directory") (builtins.readDir ./.)
13
11
);
14
12
15
15
-
buildSkill = name:
13
13
+
buildSkill =
14
14
+
name:
16
15
let
17
16
result = builtins.tryEval (pkgs.python3Packages.callPackage ./${name}/default.nix { });
18
17
in
19
18
if result.success then result.value else null;
20
19
21
21
-
skills = lib.filterAttrs (_: v: v != null) (
22
22
-
lib.genAttrs skillDirs buildSkill
23
23
-
);
20
20
+
skills = lib.filterAttrs (_: v: v != null) (lib.genAttrs skillDirs buildSkill);
24
21
in
25
22
{
26
23
# Expose via legacyPackages so `nix run .#skills.browser-cli` works
···
43
43
};
44
44
45
45
promtail_file_lagging = {
46
46
-
condition = ''abs(promtail_file_bytes_total - promtail_read_bytes_total) > 1e6'';
46
46
+
condition = "abs(promtail_file_bytes_total - promtail_read_bytes_total) > 1e6";
47
47
time = "15m";
48
48
-
description = ''{{ $labels.instance }} {{ $labels.job }} {{ $labels.path }} has been lagging by more than 1MB for more than 15m.'';
48
48
+
description = "{{ $labels.instance }} {{ $labels.job }} {{ $labels.path }} has been lagging by more than 1MB for more than 15m.";
49
49
};
50
50
51
51
filesystem_full_80percent = {
···
61
61
};
62
62
63
63
filesystem_inodes_full = {
64
64
-
condition = ''disk_inodes_free / disk_inodes_total < 0.10'';
64
64
+
condition = "disk_inodes_free / disk_inodes_total < 0.10";
65
65
time = "10m";
66
66
description = "{{$labels.instance}} device {{$labels.device}} on {{$labels.path}} got less than 10% inodes left on its filesystem.";
67
67
};
···
92
92
hash = "sha256-H+GMR3PIzLUu8jyltKCwhBs41U3dXZx9huuLBg8TLzk=";
93
93
};
94
94
in
95
95
-
stdenv.mkDerivation (finalAttrs: {
95
95
+
stdenv.mkDerivation (_finalAttrs: {
96
96
pname = "sdrpp-brown";
97
97
98
98
version = "0-unstable-2025-12-29";
···
107
107
nativeBuildInputs = [
108
108
cmake
109
109
pkg-config
110
110
-
] ++ lib.optional ch_tetra_demodulator unzip;
110
110
+
]
111
111
+
++ lib.optional ch_tetra_demodulator unzip;
111
112
112
112
-
buildInputs =
113
113
-
[
114
114
-
glfw
115
115
-
glew
116
116
-
fftw
117
117
-
fftwFloat
118
118
-
volk
119
119
-
zstd
120
120
-
]
121
121
-
++ lib.optional stdenv.hostPlatform.isLinux libX11
122
122
-
++ lib.optional stdenv.hostPlatform.isLinux libpulseaudio
123
123
-
++ lib.optional airspy_source airspy
124
124
-
++ lib.optional airspyhf_source airspyhf
125
125
-
++ lib.optional bladerf_source libbladeRF
126
126
-
++ lib.optional hackrf_source hackrf
127
127
-
++ lib.optional limesdr_source limesuite
128
128
-
++ lib.optionals rtl_sdr_source [
129
129
-
rtl-sdr-osmocom
130
130
-
libusb1
131
131
-
]
132
132
-
++ lib.optional sdrplay_source sdrplay
133
133
-
++ lib.optional soapy_source soapysdr-with-plugins
134
134
-
++ lib.optionals plutosdr_source [
135
135
-
libiio
136
136
-
libad9361
137
137
-
]
138
138
-
++ lib.optionals usrp_source [
139
139
-
uhd
140
140
-
boost
141
141
-
]
142
142
-
++ lib.optional (audio_source || audio_sink) rtaudio
143
143
-
++ lib.optional portaudio_sink portaudio
144
144
-
++ lib.optional (dab_decoder || m17_decoder) codec2;
113
113
+
buildInputs = [
114
114
+
glfw
115
115
+
glew
116
116
+
fftw
117
117
+
fftwFloat
118
118
+
volk
119
119
+
zstd
120
120
+
]
121
121
+
++ lib.optional stdenv.hostPlatform.isLinux libX11
122
122
+
++ lib.optional stdenv.hostPlatform.isLinux libpulseaudio
123
123
+
++ lib.optional airspy_source airspy
124
124
+
++ lib.optional airspyhf_source airspyhf
125
125
+
++ lib.optional bladerf_source libbladeRF
126
126
+
++ lib.optional hackrf_source hackrf
127
127
+
++ lib.optional limesdr_source limesuite
128
128
+
++ lib.optionals rtl_sdr_source [
129
129
+
rtl-sdr-osmocom
130
130
+
libusb1
131
131
+
]
132
132
+
++ lib.optional sdrplay_source sdrplay
133
133
+
++ lib.optional soapy_source soapysdr-with-plugins
134
134
+
++ lib.optionals plutosdr_source [
135
135
+
libiio
136
136
+
libad9361
137
137
+
]
138
138
+
++ lib.optionals usrp_source [
139
139
+
uhd
140
140
+
boost
141
141
+
]
142
142
+
++ lib.optional (audio_source || audio_sink) rtaudio
143
143
+
++ lib.optional portaudio_sink portaudio
144
144
+
++ lib.optional (dab_decoder || m17_decoder) codec2;
145
145
146
146
# Pre-place the ETSI codec in the build directory so CMake doesn't try to download it
147
147
preConfigure = lib.optionalString ch_tetra_demodulator ''
···
1
1
# GPD Win Mini 2025 PipeWire EQ configuration
2
2
# Based on https://github.com/aarron-lee/gpd-win-tricks/tree/main/win_mini_equalizer
3
3
# Credit: @BrotherChenwk from the GPD Discord
4
4
-
{ pkgs, lib, config, ... }:
4
4
+
{ pkgs, ... }:
5
5
6
6
let
7
7
# The ALSA output device - may need adjustment based on PCI enumeration
···
87
87
user = {
88
88
name = "radio-news";
89
89
};
90
90
-
script = ''. ${pkgs.writers.writeDash "htgen-news" ''
90
90
+
script = ". ${pkgs.writers.writeDash "htgen-news" ''
91
91
set -xefu
92
92
case "''${Method:-GET} $Request_URI" in
93
93
"GET /")
···
107
107
exit
108
108
;;
109
109
esac
110
110
-
''}'';
110
110
+
''}";
111
111
};
112
112
113
113
# debug