flake-update-20260201
1{
2 config,
3 globals,
4 libx,
5 pkgs,
6 ...
7}:
8{
9
10 imports = [
11 ../common/hardware/laptop.nix
12 ../common/programs/direnv.nix
13 ../common/programs/git.nix
14 ../common/programs/nix-ld.nix
15 ../common/programs/tmux.nix
16 # ../common/services/networkmanager.nix
17 # ../common/services/fprint.nix # With yubikey I don't really need this to be honest
18 ../common/services/ansible.nix
19 ../common/services/containers.nix
20 ../common/services/docker.nix
21 ../common/services/libvirt.nix
22 ../common/services/binfmt.nix
23 # ../common/services/oci-image-mirroring.nixi
24 # ../common/services/ollama.nix # TODO handle nvidia vs not ?
25 ../common/services/prometheus-exporters-node.nix
26 # ../common/services/gitea-runner
27
28 ../redhat
29
30 # OpenShift port forwarding
31 ./openshift-port-forward.nix
32
33 # Remote build system
34 ../../modules/job-notify
35 ../../modules/nixpkgs-consolidate
36 ../../modules/microshift
37
38 # Binary cache
39 ../../modules/harmonia
40
41 # XMPP Research Bot
42 ../../modules/xmpp-research-bot
43
44 # Automated flake updates
45 ../../modules/nix-flake-updater
46 ];
47
48 # Firewall is enabled in openshift-port-forward.nix
49 # networking.firewall.enable = false;
50
51 # Suppress malformed DHCP option 24 (MTU plateau) warnings from router
52 networking.dhcpcd.extraConfig = ''
53 nooption mtu_plateau
54 '';
55
56 # OpenShift SNO endpoints
57 networking.extraHosts = ''
58 192.168.100.7 api.ocp4.lab.home
59 192.168.100.7 api-int.ocp4.lab.home
60 192.168.100.7 console-openshift-console.apps.ocp4.lab.home
61 192.168.100.7 oauth-openshift.apps.ocp4.lab.home
62 '';
63
64 # Age secrets
65 age.secrets."ntfy-token" = {
66 file = ../../secrets/sakhalin/ntfy-token.age;
67 mode = "440";
68 owner = "root";
69 group = "users";
70 };
71 age.secrets."harmonia-aomi-signing-key" = {
72 file = ../../secrets/harmonia/aomi-signing-key.age;
73 mode = "440";
74 owner = "root";
75 group = "root";
76 };
77 age.secrets."xmpp-research-bot-password" = {
78 file = ../../secrets/aomi/xmpp-research-bot-password.age;
79 mode = "400";
80 owner = "vincent";
81 group = "users";
82 };
83 age.secrets."gemini-api-key" = {
84 file = ../../secrets/aomi/gemini-api-key.age;
85 mode = "400";
86 owner = "vincent";
87 group = "users";
88 };
89
90 # TODO make it an option ? (otherwise I'll add it for all)
91 users.users.vincent.linger = true;
92
93 # Binary cache server (x86_64-linux)
94 services.harmonia-cache = {
95 enable = true;
96 signKeyPath = config.age.secrets."harmonia-aomi-signing-key".path;
97 port = 5000;
98 workers = 4;
99 priority = 30;
100
101 # Nightly cache pre-population
102 builder = {
103 enable = true;
104 systems = [
105 "aomi" # Self
106 "kyushu" # Work laptop
107 "sakhalin" # Server
108 ];
109 schedule = "02:00"; # 2 AM daily
110 notification = {
111 enable = true;
112 tokenFile = config.age.secrets."ntfy-token".path;
113 };
114 };
115 };
116
117 # Remote build system
118 services.job-notify = {
119 enable = true;
120 ntfyServer = "https://ntfy.sbr.pm";
121 ntfyTokenFile = config.age.secrets."ntfy-token".path;
122 defaultTopic = "builds";
123 };
124
125 # Nixpkgs WIP branch consolidation automation
126 services.nixpkgs-consolidate = {
127 enable = true;
128 user = "vincent";
129 schedule = "daily";
130 ntfyTokenFile = config.age.secrets."ntfy-token".path;
131 configFile = "/home/vincent/.config/nixpkgs-automation/branches.conf";
132 };
133
134 # XMPP Research Bot (uses Vertex AI for Claude, direct API for Gemini)
135 services.xmpp-research-bot = {
136 enable = true;
137 jid = "researchbot@xmpp.sbr.pm";
138 ownerJid = "vincent@xmpp.sbr.pm";
139 passwordFile = config.age.secrets."xmpp-research-bot-password".path;
140 geminiApiKeyFile = config.age.secrets."gemini-api-key".path;
141 vertexProjectId = "itpc-gcp-pnd-pe-eng-claude";
142 vertexRegion = "us-east5";
143 inboxPath = "/home/vincent/desktop/org/inbox.org";
144 commandsPath = "/home/vincent/.config/xmpp-research-bot/commands.yaml";
145 user = "vincent";
146 group = "users";
147 };
148
149 # Automated flake.lock updates with build verification
150 services.nix-flake-updater = {
151 enable = true;
152 repoPath = "/home/vincent/src/home";
153
154 # Build systems across both architectures for verification
155 buildSystems = [
156 # x86_64-linux systems
157 "aomi" # Self (laptop/build server)
158 "kyushu" # Work laptop
159 "sakhalin" # Server
160 "kerkouane" # VPS server
161
162 # aarch64-linux systems
163 "rhea" # Main media server
164 "aion" # XMPP/podcast server
165 "athena" # Raspberry Pi 4
166 "demeter" # Raspberry Pi 4
167 "aix" # Raspberry Pi 4
168 ];
169
170 # Run weekly on Sunday at 2 AM
171 schedule = "Sun *-*-* 02:00:00";
172
173 # Notifications via ntfy
174 ntfyServer = "https://ntfy.sbr.pm";
175 ntfyTopic = "nix-updates";
176 ntfyTokenFile = config.age.secrets."ntfy-token".path;
177
178 # Git settings
179 gitRemote = "origin";
180 branchPrefix = "flake-update-";
181
182 # Run as vincent (has git push access)
183 user = "vincent";
184
185 # Add randomized delay to avoid conflicts
186 randomizedDelaySec = 1800; # 0-30 min delay
187 };
188
189 services = {
190 logind.settings.Login = {
191 HandleLidSwitch = "ignore";
192 HandleLidSwitchExternalPower = "ignore";
193 HandleLidSwitchDocked = "ignore";
194 };
195 wireguard = {
196 enable = true;
197 ips = libx.wg-ips globals.machines.aomi.net.vpn.ips;
198 endpoint = "${globals.net.vpn.endpoint}";
199 endpointPublicKey = "${globals.machines.kerkouane.net.vpn.pubkey}";
200 };
201 ollama = {
202 enable = true;
203 # acceleration = "cuda"; # no nvidia :D
204 host = "0.0.0.0"; # Listen on all interfaces for network access
205 port = 11434;
206 loadModels = [
207 # Coding Models
208 "qwen2.5-coder:7b" # Best coding: 88.4% HumanEval, Apache 2.0 (~4-5GB, 10-15 tok/s)
209 "codestral" # Latest coding (Jan 2025): 86.6% HumanEval, #1 LMsys (~14GB, 8-10 tok/s)
210
211 # Reasoning Models
212 "phi4-reasoning" # Best 14B reasoning: outperforms 70B distillation, MIT (~9GB, 6-10 tok/s)
213 "deepseek-r1:7b" # Lightweight reasoning: MIT license (~4.5GB, 8-12 tok/s)
214
215 # Multimodal
216 "qwen2.5vl:7b" # Best vision: beats Llama 3.2 11B, Apache 2.0 (~6GB, 5-8 tok/s)
217
218 # Quick Tasks
219 "phi3.5:3.8b" # Ultra-fast all-rounder: MIT license (~2.4GB, 15-25 tok/s)
220
221 # Tool Calling / OpenCode Support
222 "llama3.1:8b" # Native tool support, good for OpenCode (~4.7GB)
223 "mistral-nemo" # Fast tool calling support (~7.7GB)
224
225 # Legacy (keeping for compatibility)
226 "mistral:7b-instruct-q4_K_M" # General purpose fallback
227 ];
228 environmentVariables = {
229 OLLAMA_MODELS = "/var/lib/ollama/models";
230 OLLAMA_NUM_PARALLEL = "1"; # CPU-only, keep it simple
231 OLLAMA_KEEP_ALIVE = "10m";
232 };
233 };
234 smartd = {
235 enable = true;
236 devices = [ { device = "/dev/nvme0n1"; } ];
237 };
238 hardware.bolt.enable = true;
239 # gitea-actions-runner = {
240 # instances = {
241 # "aomi-codeberg" = {
242 # name = "aomi";
243 # enable = true;
244 # url = "https://codeberg.org";
245 # # tokenFile = "/home/vincent/sync/codeberg.token";
246 # tokenFile = "/etc/codeberg.token";
247 # labels = [
248 # # "local:host"
249 # "nixos-${pkgs.system}:host"
250 # "native:host"
251 # "docker:docker://gitea/runner-images:ubuntu-latest"
252 # "ubuntu-latest:docker://gitea/runner-images:ubuntu-latest"
253 # "ubuntu-24.04:docker://gitea/runner-images:ubuntu-24.04"
254 # "ubuntu-22.04:docker://gitea/runner-images:ubuntu-22.04"
255 # "ubuntu-20.04:docker://gitea/runner-images:ubuntu-20.04"
256 # # "nix:docker://localhost:5921/nix-runner"
257 # ];
258 # hostPackages = with pkgs; [
259 # bash
260 # direnv
261 # coreutils
262 # curl
263 # gawk
264 # nixVersions.stable
265 # gitFull
266 # gnused
267 # docker
268 # openssh
269 # wget
270 # ];
271 # };
272 # };
273 # };
274 };
275
276 # Ollama Prometheus Exporter (Docker-based, built locally)
277 systemd.tmpfiles.rules = [
278 "d /var/lib/ollama-exporter 0755 root root -"
279 "d /var/lib/git-builds 0755 builder users -"
280 ];
281
282 systemd.services.ollama-exporter = {
283 description = "Ollama Prometheus Exporter";
284 after = [
285 "docker.service"
286 "ollama.service"
287 ];
288 requires = [ "docker.service" ];
289 wantedBy = [ "multi-user.target" ];
290
291 serviceConfig = {
292 Type = "simple";
293 Restart = "always";
294 RestartSec = "10s";
295
296 ExecStartPre = [
297 # Stop and remove existing container
298 "-${pkgs.docker}/bin/docker stop ollama-exporter"
299 "-${pkgs.docker}/bin/docker rm ollama-exporter"
300 # Copy source files to build directory (for future manual rebuilds if needed)
301 "${pkgs.coreutils}/bin/cp ${../../tools/ollama-exporter/Dockerfile} /var/lib/ollama-exporter/Dockerfile"
302 "${pkgs.coreutils}/bin/cp ${../../tools/ollama-exporter/ollama_exporter.py} /var/lib/ollama-exporter/ollama_exporter.py"
303 # Build image locally only if it doesn't exist (to avoid DNS timeout issues)
304 "-${pkgs.bash}/bin/bash -c '${pkgs.docker}/bin/docker image inspect ollama-exporter:local >/dev/null 2>&1 || ${pkgs.docker}/bin/docker build -t ollama-exporter:local /var/lib/ollama-exporter'"
305 ];
306
307 ExecStart = ''
308 ${pkgs.docker}/bin/docker run --rm --name ollama-exporter \
309 -p 8000:8000 \
310 -e OLLAMA_HOST=http://localhost:11434 \
311 --network host \
312 ollama-exporter:local
313 '';
314
315 ExecStop = "${pkgs.docker}/bin/docker stop ollama-exporter";
316 };
317 };
318
319 # Open firewall for Ollama exporter
320 networking.firewall.allowedTCPPorts = [ 8000 ];
321
322 # Builder user for remote builds
323 users.users.builder = {
324 isSystemUser = true;
325 group = "users";
326 home = "/var/lib/git-builds";
327 createHome = true;
328 openssh.authorizedKeys.keys = [
329 # This will be populated with kerkouane's SSH key for build triggering
330 ];
331 };
332
333 # Build execution script
334 environment.etc."git-builds/execute-build.sh" = {
335 text = ''
336 #!${pkgs.bash}/bin/bash
337 set -euo pipefail
338
339 REPO_NAME="$1"
340 BUILD_TYPE="''${2:-auto}"
341 REPO_URL="vincent@kerkouane.sbr.pm:git/public/$REPO_NAME.git"
342
343 # Cache directory and worktree
344 CACHE_DIR="/var/lib/git-builds/$REPO_NAME.git"
345 BUILD_DIR="/var/lib/git-builds/$REPO_NAME-build-$(${pkgs.coreutils}/bin/date +%s)"
346
347 echo "Building $REPO_NAME (type: $BUILD_TYPE)"
348 echo "Cache: $CACHE_DIR"
349 echo "Build directory: $BUILD_DIR"
350
351 # Fetch or clone
352 if [ -d "$CACHE_DIR" ]; then
353 echo "Fetching updates for $REPO_NAME..."
354 cd "$CACHE_DIR" && ${pkgs.git}/bin/git fetch origin
355 else
356 echo "Cloning $REPO_NAME..."
357 ${pkgs.git}/bin/git clone --bare "$REPO_URL" "$CACHE_DIR"
358 fi
359
360 # Create worktree for isolated build
361 echo "Creating worktree at $BUILD_DIR..."
362 cd "$CACHE_DIR"
363 ${pkgs.git}/bin/git worktree add "$BUILD_DIR" main
364
365 cd "$BUILD_DIR"
366 echo "Working directory: $(pwd)"
367
368 # Execute build based on type
369 case "$BUILD_TYPE" in
370 nixos)
371 echo "Running NixOS build..."
372 ${pkgs.nixos-rebuild}/bin/nixos-rebuild build --flake .#aomi
373 ;;
374 make)
375 echo "Running make build..."
376 ${pkgs.gnumake}/bin/make build
377 ;;
378 docker)
379 echo "Running Docker build..."
380 ${pkgs.docker}/bin/docker build -t "$REPO_NAME:latest" .
381 ;;
382 go)
383 echo "Running Go build..."
384 ${pkgs.go}/bin/go build ./...
385 ;;
386 custom)
387 if [ -x .git-build.sh ]; then
388 echo "Running custom build script..."
389 ./.git-build.sh
390 else
391 echo "ERROR: .git-build.sh not found or not executable"
392 exit 1
393 fi
394 ;;
395 auto)
396 echo "Auto-detecting build type..."
397 if [ -f flake.nix ]; then
398 echo "Detected NixOS flake"
399 ${pkgs.nixos-rebuild}/bin/nixos-rebuild build --flake .#aomi
400 elif [ -f Makefile ]; then
401 echo "Detected Makefile"
402 ${pkgs.gnumake}/bin/make build
403 elif [ -f Dockerfile ]; then
404 echo "Detected Dockerfile"
405 ${pkgs.docker}/bin/docker build -t "$REPO_NAME:latest" .
406 elif [ -f go.mod ]; then
407 echo "Detected Go module"
408 ${pkgs.go}/bin/go build ./...
409 else
410 echo "ERROR: Could not auto-detect build type"
411 exit 1
412 fi
413 ;;
414 *)
415 echo "ERROR: Unknown build type: $BUILD_TYPE"
416 exit 1
417 ;;
418 esac
419
420 echo "Build completed successfully!"
421
422 # Cleanup worktree (keep cache)
423 echo "Cleaning up worktree..."
424 cd /
425 ${pkgs.git}/bin/git -C "$CACHE_DIR" worktree remove "$BUILD_DIR"
426 echo "Done!"
427 '';
428 mode = "0755";
429 };
430
431 # Allow builder to run systemd-run without password
432 security.sudo.extraRules = [
433 {
434 users = [ "builder" ];
435 commands = [
436 {
437 command = "${pkgs.systemd}/bin/systemd-run";
438 options = [ "NOPASSWD" ];
439 }
440 ];
441 }
442 ];
443
444 # MicroShift via CRC (disabled for now)
445 services.microshift = {
446 enable = false;
447 cpus = 4;
448 memory = 8192; # 8GB
449 diskSize = 40;
450 user = "vincent";
451 # pullSecret will be configured via agenix later
452 };
453
454}