close
nix machine / user configurations
6

Configure Feed

Select the types of activity you want to include in your feed.

Port focalor and valefar into ark #1

Merged opened by okami.mom targeting terra from merge-nixcfg

Ports focalor and valefar into ark. Valefar system build and NAT test pass; deployed live as generation 110 on 2026-09-28 without reboot. SSH, PVE/VMs, ZFS, mesh, Searx, CUPS, Docker etcd, and media services verified. Focalor evaluates, but full CUDA build remains pending; focalor not deployed.

Labels

None yet.

Participants 1
AT URI
at://did:plc:3rwz3xfw2crswgifqgc3g7zh/sh.tangled.repo.pull/3mwlewxldwr22
+2852 -14
Diff #1
+491 -13
flake.lock
··· 1 1 { 2 2 "nodes": { 3 + "agenix": { 4 + "inputs": { 5 + "darwin": "darwin", 6 + "home-manager": "home-manager", 7 + "nixpkgs": "nixpkgs", 8 + "systems": "systems" 9 + }, 10 + "locked": { 11 + "lastModified": 1770165109, 12 + "narHash": "sha256-9VnK6Oqai65puVJ4WYtCTvlJeXxMzAp/69HhQuTdl/I=", 13 + "owner": "ryantm", 14 + "repo": "agenix", 15 + "rev": "b027ee29d959fda4b60b57566d64c98a202e0feb", 16 + "type": "github" 17 + }, 18 + "original": { 19 + "owner": "ryantm", 20 + "repo": "agenix", 21 + "rev": "b027ee29d959fda4b60b57566d64c98a202e0feb", 22 + "type": "github" 23 + } 24 + }, 3 25 "bun2nix": { 4 26 "inputs": { 5 27 "flake-parts": [ ··· 34 56 "type": "github" 35 57 } 36 58 }, 59 + "catppuccin": { 60 + "inputs": { 61 + "nixpkgs": "nixpkgs_2" 62 + }, 63 + "locked": { 64 + "lastModified": 1789553013, 65 + "narHash": "sha256-W5dvgFOuVs24X3G5tUb8C2IHU7ICXNvyGPiWWFjfbuo=", 66 + "owner": "catppuccin", 67 + "repo": "nix", 68 + "rev": "89b3eacf59d6b5eefbc2d69c3a4eb5aaf66d63bc", 69 + "type": "github" 70 + }, 71 + "original": { 72 + "owner": "catppuccin", 73 + "repo": "nix", 74 + "type": "github" 75 + } 76 + }, 37 77 "chaotic": { 38 78 "inputs": { 39 79 "flake-schemas": "flake-schemas", 40 - "home-manager": "home-manager", 41 - "nixpkgs": "nixpkgs" 80 + "home-manager": "home-manager_2", 81 + "nixpkgs": "nixpkgs_3" 42 82 }, 43 83 "locked": { 44 84 "lastModified": 1788855501, ··· 70 110 "type": "github" 71 111 } 72 112 }, 113 + "darwin": { 114 + "inputs": { 115 + "nixpkgs": [ 116 + "agenix", 117 + "nixpkgs" 118 + ] 119 + }, 120 + "locked": { 121 + "lastModified": 1744478979, 122 + "narHash": "sha256-dyN+teG9G82G+m+PX/aSAagkC+vUv0SgUw3XkPhQodQ=", 123 + "owner": "lnl7", 124 + "repo": "nix-darwin", 125 + "rev": "43975d782b418ebf4969e9ccba82466728c2851b", 126 + "type": "github" 127 + }, 128 + "original": { 129 + "owner": "lnl7", 130 + "ref": "master", 131 + "repo": "nix-darwin", 132 + "type": "github" 133 + } 134 + }, 135 + "flake-compat": { 136 + "flake": false, 137 + "locked": { 138 + "lastModified": 1767039857, 139 + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", 140 + "owner": "NixOS", 141 + "repo": "flake-compat", 142 + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", 143 + "type": "github" 144 + }, 145 + "original": { 146 + "owner": "NixOS", 147 + "repo": "flake-compat", 148 + "type": "github" 149 + } 150 + }, 151 + "flake-compat_2": { 152 + "locked": { 153 + "lastModified": 1767039857, 154 + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", 155 + "owner": "edolstra", 156 + "repo": "flake-compat", 157 + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", 158 + "type": "github" 159 + }, 160 + "original": { 161 + "owner": "edolstra", 162 + "repo": "flake-compat", 163 + "type": "github" 164 + } 165 + }, 73 166 "flake-parts": { 74 167 "inputs": { 75 168 "nixpkgs-lib": [ ··· 91 184 "type": "github" 92 185 } 93 186 }, 187 + "flake-parts_2": { 188 + "inputs": { 189 + "nixpkgs-lib": "nixpkgs-lib" 190 + }, 191 + "locked": { 192 + "lastModified": 1788450739, 193 + "narHash": "sha256-glZLQlzIn1fXH6PazR2iUmTo7kzzyYSshrWhLS9TqCU=", 194 + "owner": "hercules-ci", 195 + "repo": "flake-parts", 196 + "rev": "31729ca8cbdb4fa927b34e5f4353e6a83f39e993", 197 + "type": "github" 198 + }, 199 + "original": { 200 + "owner": "hercules-ci", 201 + "repo": "flake-parts", 202 + "type": "github" 203 + } 204 + }, 205 + "flake-parts_3": { 206 + "inputs": { 207 + "nixpkgs-lib": "nixpkgs-lib_2" 208 + }, 209 + "locked": { 210 + "lastModified": 1772408722, 211 + "narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=", 212 + "owner": "hercules-ci", 213 + "repo": "flake-parts", 214 + "rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3", 215 + "type": "github" 216 + }, 217 + "original": { 218 + "owner": "hercules-ci", 219 + "repo": "flake-parts", 220 + "type": "github" 221 + } 222 + }, 94 223 "flake-schemas": { 95 224 "locked": { 96 225 "lastModified": 1780327564, ··· 107 236 }, 108 237 "flake-utils": { 109 238 "inputs": { 110 - "systems": "systems_2" 239 + "systems": "systems_3" 111 240 }, 112 241 "locked": { 113 242 "lastModified": 1731533236, ··· 123 252 "type": "github" 124 253 } 125 254 }, 255 + "git-hooks": { 256 + "inputs": { 257 + "flake-compat": [ 258 + "nix-gaming", 259 + "flake-compat" 260 + ], 261 + "nixpkgs": [ 262 + "nix-gaming", 263 + "nixpkgs" 264 + ] 265 + }, 266 + "locked": { 267 + "lastModified": 1790091288, 268 + "narHash": "sha256-2dUuLTiQrf2gUFVLlayDq/hgluitplAMv6Y9bYwQQ+c=", 269 + "owner": "cachix", 270 + "repo": "git-hooks.nix", 271 + "rev": "0d3997c4d3253505f77c9bcea63904bb575da3c5", 272 + "type": "github" 273 + }, 274 + "original": { 275 + "owner": "cachix", 276 + "repo": "git-hooks.nix", 277 + "type": "github" 278 + } 279 + }, 126 280 "home-manager": { 281 + "inputs": { 282 + "nixpkgs": [ 283 + "agenix", 284 + "nixpkgs" 285 + ] 286 + }, 287 + "locked": { 288 + "lastModified": 1745494811, 289 + "narHash": "sha256-YZCh2o9Ua1n9uCvrvi5pRxtuVNml8X2a03qIFfRKpFs=", 290 + "owner": "nix-community", 291 + "repo": "home-manager", 292 + "rev": "abfad3d2958c9e6300a883bd443512c55dfeb1be", 293 + "type": "github" 294 + }, 295 + "original": { 296 + "owner": "nix-community", 297 + "repo": "home-manager", 298 + "type": "github" 299 + } 300 + }, 301 + "home-manager_2": { 127 302 "inputs": { 128 303 "nixpkgs": [ 129 304 "chaotic", ··· 144 319 "type": "github" 145 320 } 146 321 }, 322 + "home-manager_3": { 323 + "inputs": { 324 + "nixpkgs": [ 325 + "zen-browser", 326 + "nixpkgs" 327 + ] 328 + }, 329 + "locked": { 330 + "lastModified": 1789430117, 331 + "narHash": "sha256-t+U1mijItLJ64xZOifpfQ17kpAL73nU7pDI48ScacVc=", 332 + "owner": "nix-community", 333 + "repo": "home-manager", 334 + "rev": "cda90fd8838825c689fde9d3f3b4e937937790df", 335 + "type": "github" 336 + }, 337 + "original": { 338 + "owner": "nix-community", 339 + "repo": "home-manager", 340 + "type": "github" 341 + } 342 + }, 147 343 "llm-agents": { 148 344 "inputs": { 149 345 "bun2nix": "bun2nix", 150 346 "flake-parts": "flake-parts", 151 - "nixpkgs": "nixpkgs_2", 152 - "systems": "systems", 347 + "nixpkgs": "nixpkgs_4", 348 + "systems": "systems_2", 153 349 "treefmt-nix": "treefmt-nix" 154 350 }, 155 351 "locked": { ··· 169 365 "llm-bridge": { 170 366 "inputs": { 171 367 "flake-utils": "flake-utils", 172 - "nixpkgs": "nixpkgs_3" 368 + "nixpkgs": "nixpkgs_5" 173 369 }, 174 370 "locked": { 175 371 "lastModified": 1790067159, ··· 187 383 }, 188 384 "meowtd": { 189 385 "inputs": { 190 - "nixpkgs": "nixpkgs_4" 386 + "nixpkgs": "nixpkgs_6" 191 387 }, 192 388 "locked": { 193 389 "lastModified": 1785527457, ··· 203 399 "url": "https://git.koi.rip/koi/meowtd" 204 400 } 205 401 }, 402 + "niri": { 403 + "inputs": { 404 + "nixpkgs": [ 405 + "nixpkgs" 406 + ] 407 + }, 408 + "locked": { 409 + "lastModified": 1790353586, 410 + "narHash": "sha256-oEvDG8PTqiy6lvKKWj9D+XZyPzYcl4rm5Qs7qKR0pSk=", 411 + "owner": "niri-wm", 412 + "repo": "niri", 413 + "rev": "1f03391ea644c2a43597de7f637269e26d1e1b49", 414 + "type": "github" 415 + }, 416 + "original": { 417 + "owner": "niri-wm", 418 + "repo": "niri", 419 + "type": "github" 420 + } 421 + }, 422 + "nix-gaming": { 423 + "inputs": { 424 + "flake-compat": "flake-compat", 425 + "flake-parts": "flake-parts_2", 426 + "git-hooks": "git-hooks", 427 + "nixpkgs": [ 428 + "nixpkgs" 429 + ] 430 + }, 431 + "locked": { 432 + "lastModified": 1790483338, 433 + "narHash": "sha256-srUVCAD22Bcbg6GJbZEijDI22HMD6Lo2qOoUKtH22dg=", 434 + "owner": "fufexan", 435 + "repo": "nix-gaming", 436 + "rev": "b193ce18777d01469dbeb3b9c4110de2426e0edf", 437 + "type": "github" 438 + }, 439 + "original": { 440 + "owner": "fufexan", 441 + "repo": "nix-gaming", 442 + "type": "github" 443 + } 444 + }, 206 445 "nixpkgs": { 446 + "locked": { 447 + "lastModified": 1754028485, 448 + "narHash": "sha256-IiiXB3BDTi6UqzAZcf2S797hWEPCRZOwyNThJIYhUfk=", 449 + "owner": "NixOS", 450 + "repo": "nixpkgs", 451 + "rev": "59e69648d345d6e8fef86158c555730fa12af9de", 452 + "type": "github" 453 + }, 454 + "original": { 455 + "owner": "NixOS", 456 + "ref": "nixos-25.05", 457 + "repo": "nixpkgs", 458 + "type": "github" 459 + } 460 + }, 461 + "nixpkgs-lib": { 462 + "locked": { 463 + "lastModified": 1788057806, 464 + "narHash": "sha256-DTQSMxzDWmT0zhguthvegnVkn7CFqGCv4IHCzk5ZUpM=", 465 + "owner": "nix-community", 466 + "repo": "nixpkgs.lib", 467 + "rev": "596e2e3940e09b2abbeb03f75fa1828c57fcd72c", 468 + "type": "github" 469 + }, 470 + "original": { 471 + "owner": "nix-community", 472 + "repo": "nixpkgs.lib", 473 + "type": "github" 474 + } 475 + }, 476 + "nixpkgs-lib_2": { 477 + "locked": { 478 + "lastModified": 1772328832, 479 + "narHash": "sha256-e+/T/pmEkLP6BHhYjx6GmwP5ivonQQn0bJdH9YrRB+Q=", 480 + "owner": "nix-community", 481 + "repo": "nixpkgs.lib", 482 + "rev": "c185c7a5e5dd8f9add5b2f8ebeff00888b070742", 483 + "type": "github" 484 + }, 485 + "original": { 486 + "owner": "nix-community", 487 + "repo": "nixpkgs.lib", 488 + "type": "github" 489 + } 490 + }, 491 + "nixpkgs-libvncserver": { 492 + "locked": { 493 + "lastModified": 1750111231, 494 + "narHash": "sha256-3a7Tha/RwYlzH/v3PJrG7+HjOj4c6YOv2K8sqdGsHVQ=", 495 + "owner": "NixOS", 496 + "repo": "nixpkgs", 497 + "rev": "e6f23dc08d3624daab7094b701aa3954923c6bbb", 498 + "type": "github" 499 + }, 500 + "original": { 501 + "owner": "NixOS", 502 + "repo": "nixpkgs", 503 + "rev": "e6f23dc08d3624daab7094b701aa3954923c6bbb", 504 + "type": "github" 505 + } 506 + }, 507 + "nixpkgs-stable": { 508 + "locked": { 509 + "lastModified": 1787753485, 510 + "narHash": "sha256-BZWCi9ZRJiARTuKTbbtvFTj7t1TK4G3UEckT3HyNfRg=", 511 + "owner": "NixOS", 512 + "repo": "nixpkgs", 513 + "rev": "062346a6d85bc4b49dfaa61c986e9c5be21217d1", 514 + "type": "github" 515 + }, 516 + "original": { 517 + "owner": "NixOS", 518 + "ref": "nixos-26.05", 519 + "repo": "nixpkgs", 520 + "type": "github" 521 + } 522 + }, 523 + "nixpkgs_2": { 524 + "locked": { 525 + "lastModified": 1789044656, 526 + "narHash": "sha256-sDGcZgdRVR58ceKz0RmkBtdUomBvu5vzbf6Aw5rUCo0=", 527 + "rev": "1927682e0d808b4a695910f76562b11e2ddecab4", 528 + "type": "tarball", 529 + "url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.11pre1070934.1927682e0d80/nixexprs.tar.xz" 530 + }, 531 + "original": { 532 + "type": "tarball", 533 + "url": "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz" 534 + } 535 + }, 536 + "nixpkgs_3": { 207 537 "locked": { 208 538 "lastModified": 1788752844, 209 539 "narHash": "sha256-VaWGJ6+cIYN2erfSecbRV+4ljI185Ty2wUrXyvQbgOw=", ··· 219 549 "type": "github" 220 550 } 221 551 }, 222 - "nixpkgs_2": { 552 + "nixpkgs_4": { 223 553 "locked": { 224 554 "lastModified": 1788894124, 225 555 "narHash": "sha256-guyexwrrF5GBKqjO0eg9LNIvrXn4j2frNak63sDr8zg=", ··· 235 565 "type": "github" 236 566 } 237 567 }, 238 - "nixpkgs_3": { 568 + "nixpkgs_5": { 239 569 "locked": { 240 570 "lastModified": 1779560665, 241 571 "narHash": "sha256-tpyBcxPpcQb8ukyNF7DoCwfSY3VPsxHoYwj00Cayv5o=", ··· 251 581 "type": "github" 252 582 } 253 583 }, 254 - "nixpkgs_4": { 584 + "nixpkgs_6": { 255 585 "locked": { 256 586 "lastModified": 1781074563, 257 587 "narHash": "sha256-md8WlXOlfnIeHeOScMTTHFyf2d6iaTwPl2apR5EQ3P4=", ··· 266 596 "type": "indirect" 267 597 } 268 598 }, 269 - "nixpkgs_5": { 599 + "nixpkgs_7": { 270 600 "locked": { 271 601 "lastModified": 1788881743, 272 602 "narHash": "sha256-151taSq/21cxagiIu7hyMVl68+FbHfwBP4lh6TB6KOM=", ··· 279 609 "url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz" 280 610 } 281 611 }, 612 + "nixpkgs_8": { 613 + "locked": { 614 + "lastModified": 1790323409, 615 + "narHash": "sha256-m4DGo58Fza5ImOegtWOeE18nhpSO1IGzsVA6Lpsb4zw=", 616 + "rev": "e94cb152ed51bd6e24eb4a41f1460252beb52cd2", 617 + "type": "tarball", 618 + "url": "https://releases.nixos.org/nixos/unstable/nixos-26.11pre1079315.e94cb152ed51/nixexprs.tar.zst" 619 + }, 620 + "original": { 621 + "type": "tarball", 622 + "url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.zst" 623 + } 624 + }, 625 + "noctalia": { 626 + "inputs": { 627 + "nixpkgs": "nixpkgs_8" 628 + }, 629 + "locked": { 630 + "lastModified": 1790523212, 631 + "narHash": "sha256-Z8SuI0YgAZaF0sumKPBF85PxPtTjpo8jvtphbgoITv8=", 632 + "owner": "noctalia-dev", 633 + "repo": "noctalia", 634 + "rev": "08c30392b1350b4f3d3fb77e23732560559f1638", 635 + "type": "github" 636 + }, 637 + "original": { 638 + "owner": "noctalia-dev", 639 + "ref": "cachix", 640 + "repo": "noctalia", 641 + "type": "github" 642 + } 643 + }, 644 + "proxmox-nixos": { 645 + "inputs": { 646 + "flake-compat": "flake-compat_2", 647 + "nixpkgs-libvncserver": "nixpkgs-libvncserver", 648 + "nixpkgs-stable": "nixpkgs-stable", 649 + "utils": "utils" 650 + }, 651 + "locked": { 652 + "lastModified": 1789217472, 653 + "narHash": "sha256-5+iviW11rRXppx5/oTkErbauwk+9e3XhENhdKTG6QJI=", 654 + "owner": "SaumonNet", 655 + "repo": "proxmox-nixos", 656 + "rev": "fc773dcf59bf188fcc806c78af635e5917ca2983", 657 + "type": "github" 658 + }, 659 + "original": { 660 + "owner": "SaumonNet", 661 + "repo": "proxmox-nixos", 662 + "type": "github" 663 + } 664 + }, 282 665 "ratlogin": { 283 666 "inputs": { 284 667 "crane": "crane", ··· 304 687 }, 305 688 "root": { 306 689 "inputs": { 690 + "agenix": "agenix", 691 + "catppuccin": "catppuccin", 307 692 "chaotic": "chaotic", 308 693 "llm-agents": "llm-agents", 309 694 "llm-bridge": "llm-bridge", 310 695 "meowtd": "meowtd", 311 - "nixpkgs": "nixpkgs_5", 312 - "ratlogin": "ratlogin" 696 + "niri": "niri", 697 + "nix-gaming": "nix-gaming", 698 + "nixpkgs": "nixpkgs_7", 699 + "noctalia": "noctalia", 700 + "proxmox-nixos": "proxmox-nixos", 701 + "ratlogin": "ratlogin", 702 + "vscode-server": "vscode-server", 703 + "zen-browser": "zen-browser" 313 704 } 314 705 }, 315 706 "rust-overlay": { ··· 363 754 "type": "github" 364 755 } 365 756 }, 757 + "systems_3": { 758 + "locked": { 759 + "lastModified": 1681028828, 760 + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 761 + "owner": "nix-systems", 762 + "repo": "default", 763 + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 764 + "type": "github" 765 + }, 766 + "original": { 767 + "owner": "nix-systems", 768 + "repo": "default", 769 + "type": "github" 770 + } 771 + }, 772 + "systems_4": { 773 + "locked": { 774 + "lastModified": 1681028828, 775 + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 776 + "owner": "nix-systems", 777 + "repo": "default", 778 + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 779 + "type": "github" 780 + }, 781 + "original": { 782 + "owner": "nix-systems", 783 + "repo": "default", 784 + "type": "github" 785 + } 786 + }, 366 787 "tranquil": { 367 788 "inputs": { 368 789 "nixpkgs": [ ··· 405 826 "repo": "treefmt-nix", 406 827 "type": "github" 407 828 } 829 + }, 830 + "utils": { 831 + "inputs": { 832 + "systems": "systems_4" 833 + }, 834 + "locked": { 835 + "lastModified": 1731533236, 836 + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 837 + "owner": "numtide", 838 + "repo": "flake-utils", 839 + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 840 + "type": "github" 841 + }, 842 + "original": { 843 + "owner": "numtide", 844 + "repo": "flake-utils", 845 + "type": "github" 846 + } 847 + }, 848 + "vscode-server": { 849 + "inputs": { 850 + "flake-parts": "flake-parts_3" 851 + }, 852 + "locked": { 853 + "lastModified": 1784312229, 854 + "narHash": "sha256-2uHCSUw341o3my1R0U0YCfbnMEazylxb58evWsjGL50=", 855 + "owner": "nix-community", 856 + "repo": "nixos-vscode-server", 857 + "rev": "2f984dfbe7e5271b5c413d3e734374cc1306c921", 858 + "type": "github" 859 + }, 860 + "original": { 861 + "owner": "nix-community", 862 + "repo": "nixos-vscode-server", 863 + "type": "github" 864 + } 865 + }, 866 + "zen-browser": { 867 + "inputs": { 868 + "home-manager": "home-manager_3", 869 + "nixpkgs": [ 870 + "nixpkgs" 871 + ] 872 + }, 873 + "locked": { 874 + "lastModified": 1790568199, 875 + "narHash": "sha256-h3AHjsOZr9iBEkNfK+Zh7/DoN5iMRpJd/6h/5NxCz9I=", 876 + "owner": "0xc000022070", 877 + "repo": "zen-browser-flake", 878 + "rev": "e50ed94ebf28bae95397e482dbb1c020f50c20c1", 879 + "type": "github" 880 + }, 881 + "original": { 882 + "owner": "0xc000022070", 883 + "repo": "zen-browser-flake", 884 + "type": "github" 885 + } 408 886 } 409 887 }, 410 888 "root": "root",
+12
flake.nix
··· 17 17 inputs.ratlogin.url = "git+https://tangled.org/ptr.pet/ratlogin"; 18 18 inputs.ratlogin.inputs.nixpkgs.follows = "nixpkgs"; 19 19 20 + inputs.agenix.url = "github:ryantm/agenix/b027ee29d959fda4b60b57566d64c98a202e0feb"; 21 + inputs.proxmox-nixos.url = "github:SaumonNet/proxmox-nixos"; 22 + inputs.vscode-server.url = "github:nix-community/nixos-vscode-server"; 23 + inputs.catppuccin.url = "github:catppuccin/nix"; 24 + inputs.noctalia.url = "github:noctalia-dev/noctalia/cachix"; 25 + inputs.niri.url = "github:niri-wm/niri"; 26 + inputs.niri.inputs.nixpkgs.follows = "nixpkgs"; 27 + inputs.nix-gaming.url = "github:fufexan/nix-gaming"; 28 + inputs.nix-gaming.inputs.nixpkgs.follows = "nixpkgs"; 29 + inputs.zen-browser.url = "github:0xc000022070/zen-browser-flake"; 30 + inputs.zen-browser.inputs.nixpkgs.follows = "nixpkgs"; 31 + 20 32 outputs = 21 33 flakeInputs: 22 34 let
+6
hosts/default.nix
··· 36 36 chernobog = allPkgsSets.x86_64-linux; 37 37 trimounts = allPkgsSets.x86_64-linux; 38 38 pupos = allPkgsSets.x86_64-linux; 39 + focalor = allPkgsSets.x86_64-linux; 40 + valefar = allPkgsSets.x86_64-linux // { 41 + pkgs = allPkgsSets.x86_64-linux.pkgs.appendOverlays [ 42 + allPkgsSets.x86_64-linux.inputs.proxmox-nixos.overlays.x86_64-linux 43 + ]; 44 + }; 39 45 }; 40 46 in 41 47 lib.mapAttrs mkSystem systems
+115
hosts/focalor/default.nix
··· 1 + { 2 + inputs, 3 + lib, 4 + pkgs, 5 + tlib, 6 + ... 7 + }: 8 + let 9 + system = pkgs.stdenv.hostPlatform.system; 10 + in 11 + { 12 + imports = [ 13 + "${inputs.home}/nixos" 14 + inputs.catppuccin.nixosModules.catppuccin 15 + inputs.nix-gaming.nixosModules.platformOptimizations 16 + inputs.noctalia.nixosModules.default 17 + inputs.vscode-server.nixosModules.default 18 + ../../modules 19 + ../../modules/stylix-null.nix 20 + ../../users/regent 21 + ./hardware.nix 22 + ] 23 + ++ (tlib.importFolder (toString ./modules)); 24 + 25 + home-manager = { 26 + useGlobalPkgs = true; 27 + backupFileExtension = "HMBackup"; 28 + extraSpecialArgs = { inherit inputs system; }; 29 + users.regent.imports = [ 30 + ../../users/regent/home.nix 31 + inputs.catppuccin.homeModules.catppuccin 32 + inputs.noctalia.homeModules.default 33 + ]; 34 + }; 35 + 36 + modules.llama-cpp.enable = true; 37 + 38 + system.stateVersion = "25.05"; 39 + catppuccin = { 40 + enable = false; 41 + autoEnable = false; 42 + }; 43 + 44 + boot = { 45 + binfmt.emulatedSystems = [ "aarch64-linux" ]; 46 + kernelModules = [ "nct6775" ]; 47 + loader = { 48 + systemd-boot.enable = true; 49 + efi.canTouchEfiVariables = true; 50 + }; 51 + supportedFilesystems = [ "nfs" ]; 52 + }; 53 + boot.kernel.sysctl."net.ipv4.ip_forward" = 1; 54 + nix.settings = { 55 + trusted-users = lib.mkForce [ "root" ]; 56 + extra-platforms = [ "aarch64-linux" ]; 57 + extra-substituters = [ 58 + "https://noctalia.cachix.org" 59 + "https://cache.numtide.com" 60 + ]; 61 + extra-trusted-public-keys = [ 62 + "noctalia.cachix.org-1:pCOR47nnMEo5thcxNDtzWpOxNFQsBRglJzxWPp3dkU4=" 63 + "niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g=" 64 + ]; 65 + }; 66 + 67 + time.timeZone = "America/New_York"; 68 + i18n.defaultLocale = "en_US.UTF-8"; 69 + environment.variables.EDITOR = lib.mkForce "vim"; 70 + programs.nix-ld.enable = true; 71 + 72 + environment.systemPackages = with pkgs; [ 73 + inputs.agenix.packages.${system}.default 74 + (prismlauncher.override { 75 + jdks = [ 76 + jdk8 77 + jdk11 78 + jdk17 79 + jdk21 80 + jdk25 81 + ]; 82 + }) 83 + temurin-bin 84 + signal-desktop 85 + google-chrome 86 + osu-lazer-bin 87 + qpwgraph 88 + easyeffects 89 + pavucontrol 90 + inputs.llm-agents.packages.${system}.omp 91 + inputs.llm-agents.packages.${system}.pi 92 + inputs.llm-agents.packages.${system}.beads 93 + imagemagick 94 + smartmontools 95 + ffmpeg 96 + nodejs_26 97 + vim 98 + wget 99 + fastfetch 100 + lsof 101 + btop 102 + git 103 + openssl 104 + stdenv 105 + gnumake 106 + parted 107 + zfs 108 + nixos-generators 109 + sqlite 110 + bun 111 + inputs.llm-agents.packages.${system}.prime-agent 112 + unzip 113 + uv 114 + ]; 115 + }
+58
hosts/focalor/hardware.nix
··· 1 + { 2 + config, 3 + lib, 4 + modulesPath, 5 + ... 6 + }: 7 + { 8 + imports = [ (modulesPath + "/installer/scan/not-detected.nix") ]; 9 + 10 + boot.initrd.availableKernelModules = [ 11 + "nvme" 12 + "xhci_pci" 13 + "ahci" 14 + "uas" 15 + "usbhid" 16 + "sd_mod" 17 + ]; 18 + boot.kernelModules = [ "kvm-amd" ]; 19 + 20 + fileSystems = { 21 + "/" = { 22 + device = "/dev/sda2"; 23 + fsType = "btrfs"; 24 + options = [ 25 + "subvol=root" 26 + "compress=zstd:3" 27 + "noatime" 28 + ]; 29 + }; 30 + "/home" = { 31 + device = "/dev/sda2"; 32 + fsType = "btrfs"; 33 + options = [ 34 + "subvol=home" 35 + "compress=zstd:3" 36 + "noatime" 37 + ]; 38 + }; 39 + "/nix" = { 40 + device = "/dev/sda2"; 41 + fsType = "btrfs"; 42 + options = [ 43 + "subvol=nix" 44 + "compress=zstd:3" 45 + "noatime" 46 + ]; 47 + }; 48 + "/boot" = { 49 + device = "/dev/disk/by-uuid/3F27-30E5"; 50 + fsType = "vfat"; 51 + options = [ "umask=0077" ]; 52 + }; 53 + }; 54 + 55 + swapDevices = [ ]; 56 + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 57 + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; 58 + }
+14
hosts/focalor/modules/dawn.nix
··· 1 + { pkgs, ... }: 2 + { 3 + users.users.dawn = { 4 + isNormalUser = true; 5 + createHome = true; 6 + home = "/home/dawn"; 7 + extraGroups = [ "wheel" ]; 8 + shell = pkgs.bashInteractive; 9 + hashedPassword = "$y$j9T$TxLlqj0RWsBtIrEhOTyqh1$mfvSCn5j7VAUymWe2/qUTB7.JdwXbqF5qWqUjqQCMu3"; 10 + openssh.authorizedKeys.keys = [ 11 + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDUeDBW4hnHgnT0SjVFeGDztht9owObSmiyWXmmIEGQp2IMPqFpCxkOU61osvfCf4ZT92Ok9iJTohLwFBvHJRD/+CH8/b54sgFAx1lLObkJOMLz4iWZ5y4fNtBYuIA2McQSQoMDpDz6TDym7v7HF7zoUyBmfHMT/9WiX/z6Ft9hY63eh9DcF7WVURzeUvXLApt9wUYUxxdC2KZ/VrDPrIOxCcOgj3le+1zTiD8zwfAGhkzRD3IEx0yCYK6oztrh6WTwA5ZW+cLziH2sVEvSHFa2O398gIvZpzsdYTcQt06d/oyZIvftcpxD8IvjpGgHEsN/mAg0ovexyqAVk+TV/1XySKaoPPVCekap0R50CVD9kEk+GlD78XBYi++aAMIq0/D+NOXkgksfODt3yJPPzQx4KH8gcn0dQJM5zeyTwDfclzMRqCwL1eVHY00EbtG9IcLmMsWk/lM6vpHfyHqHlqNJ3CnUuDBccz9p5ORC1cuj4r9CmXPPmh7OYk7gGiQb4oxuqsYClzp93qmU7qMvGwmxBJaVagNIJgBqb5fsne0OMlcer5CH4L31ozszkSkzCXtFWNoTdgQHU1J3DxxL9WQJCfKku4EPJadYOh80USnauOke5CqfsGtf6uMq4l5Ylcc1QcNhRqxpeTLAIZx0EYDhmQ4eGjAZbiv6ddUp9dAdiQ==" 12 + ]; 13 + }; 14 + }
+155
hosts/focalor/modules/desktop.nix
··· 1 + { 2 + config, 3 + inputs, 4 + lib, 5 + pkgs, 6 + ... 7 + }: 8 + let 9 + system = pkgs.stdenv.hostPlatform.system; 10 + in 11 + { 12 + programs = { 13 + niri = { 14 + enable = true; 15 + package = inputs.niri.packages.${system}.niri; 16 + }; 17 + noctalia = { 18 + enable = true; 19 + recommendedServices.enable = true; 20 + }; 21 + steam = { 22 + enable = true; 23 + platformOptimizations.enable = true; 24 + }; 25 + gamescope.enable = true; 26 + dconf.enable = true; 27 + obs-studio = { 28 + enable = true; 29 + enableVirtualCamera = true; 30 + plugins = [ pkgs.obs-studio-plugins.droidcam-obs ]; 31 + }; 32 + }; 33 + 34 + services = { 35 + greetd = { 36 + enable = true; 37 + settings.default_session = { 38 + command = "${pkgs.tuigreet}/bin/tuigreet --time --remember --cmd niri-session"; 39 + user = "greeter"; 40 + }; 41 + }; 42 + xserver.enable = true; 43 + displayManager.sddm.enable = true; 44 + displayManager.defaultSession = lib.mkForce "niri"; 45 + desktopManager.plasma6.enable = true; 46 + xrdp = { 47 + enable = true; 48 + defaultWindowManager = "startplasma-x11"; 49 + openFirewall = true; 50 + }; 51 + dbus.enable = true; 52 + gvfs.enable = true; 53 + gnome.gnome-keyring.enable = true; 54 + upower.enable = true; 55 + power-profiles-daemon.enable = true; 56 + blueman.enable = true; 57 + pipewire = { 58 + enable = true; 59 + alsa.enable = true; 60 + alsa.support32Bit = true; 61 + pulse.enable = true; 62 + }; 63 + }; 64 + 65 + security = { 66 + polkit.enable = true; 67 + rtkit.enable = true; 68 + pam.services.greetd.enableGnomeKeyring = true; 69 + }; 70 + 71 + hardware = { 72 + bluetooth = { 73 + enable = true; 74 + powerOnBoot = true; 75 + }; 76 + graphics.enable = true; 77 + nvidia = { 78 + modesetting.enable = true; 79 + powerManagement.enable = false; 80 + powerManagement.finegrained = false; 81 + open = true; 82 + nvidiaSettings = true; 83 + package = config.boot.kernelPackages.nvidiaPackages.latest; 84 + }; 85 + }; 86 + services.xserver.videoDrivers = [ "nvidia" ]; 87 + 88 + environment = { 89 + variables = { 90 + GBM_BACKEND = "nvidia-drm"; 91 + __GLX_VENDOR_LIBRARY_NAME = "nvidia"; 92 + }; 93 + sessionVariables.NIXOS_OZONE_WL = "1"; 94 + systemPackages = with pkgs; [ 95 + kitty 96 + vscode 97 + zed-editor 98 + fastfetch 99 + hyfetch 100 + pamixer 101 + zellij 102 + firefox 103 + chromium 104 + kpcli 105 + eyedropper 106 + krita 107 + thunar 108 + libreoffice 109 + signal-desktop 110 + haruna 111 + (symlinkJoin { 112 + name = "equibop-wrapped"; 113 + paths = [ equibop ]; 114 + nativeBuildInputs = [ makeWrapper ]; 115 + postBuild = '' 116 + wrapProgram $out/bin/equibop \ 117 + --add-flags "--disable-features=WebRtcAllowInputVolumeAdjustment" 118 + ''; 119 + }) 120 + inputs.zen-browser.packages.${system}.default 121 + grim 122 + slurp 123 + wl-clipboard 124 + xwayland-satellite 125 + ]; 126 + }; 127 + 128 + fonts = { 129 + packages = with pkgs; [ 130 + nerd-fonts.fira-code 131 + comic-neue 132 + comic-mono 133 + corefonts 134 + ]; 135 + fontconfig.defaultFonts = { 136 + sansSerif = [ 137 + "Comic Neue" 138 + "Comic Sans MS" 139 + ]; 140 + serif = [ 141 + "Comic Neue" 142 + "Comic Sans MS" 143 + ]; 144 + monospace = [ "Comic Mono" ]; 145 + }; 146 + }; 147 + 148 + xdg.portal = { 149 + enable = true; 150 + extraPortals = with pkgs; [ 151 + xdg-desktop-portal-gtk 152 + xdg-desktop-portal-gnome 153 + ]; 154 + }; 155 + }
+48
hosts/focalor/modules/llama-cpp.nix
··· 1 + { 2 + config, 3 + lib, 4 + pkgs, 5 + ... 6 + }: 7 + 8 + let 9 + cfg = config.modules.llama-cpp; 10 + llamaCppCuda = pkgs.llama-cpp.override { cudaSupport = true; }; 11 + in 12 + { 13 + options.modules.llama-cpp.enable = lib.mkEnableOption "the llama.cpp Gemma service"; 14 + 15 + config = lib.mkIf cfg.enable { 16 + environment.systemPackages = [ llamaCppCuda ]; 17 + 18 + networking.firewall.interfaces.br0.allowedTCPPorts = [ 8080 ]; 19 + 20 + systemd.services.llama-gemma = { 21 + description = "Gemma 4 12B Unified via llama.cpp"; 22 + after = [ "network-online.target" ]; 23 + wants = [ "network-online.target" ]; 24 + wantedBy = [ "multi-user.target" ]; 25 + 26 + environment = { 27 + HOME = "/home/regent"; 28 + XDG_CACHE_HOME = "/home/regent/.cache"; 29 + CUDA_VISIBLE_DEVICES = "1"; 30 + LD_LIBRARY_PATH = "/run/opengl-driver/lib:/run/opengl-driver-32/lib"; 31 + }; 32 + 33 + serviceConfig = { 34 + Type = "simple"; 35 + User = "regent"; 36 + Group = "users"; 37 + WorkingDirectory = "/home/regent"; 38 + ExecStart = '' 39 + /home/regent/Developer/llama.cpp-gemma4/build-cuda-gcc14/bin/llama-server -m /home/regent/models/gemma-4-12b-unsloth-2026-07-17/gemma-4-12b-it-Q4_K_M.gguf --mmproj /home/regent/models/gemma-4-12b-unsloth-2026-07-17/mmproj-F16.gguf --lora-scaled /home/regent/Developer/web-extract-sft/artifacts/runs/query-preview-gemma4-12b-r16a8-v11/adapters/step192-f16.gguf:0.5 --model-draft /home/regent/models/gemma-4-12b/gemma-4-12B-it-qat-assistant-MTP-Q8_0.gguf --spec-type draft-mtp --spec-draft-n-max 4 --alias gemma-4-12b,gemma4 --host 10.0.0.13 --port 8080 --device CUDA0 --split-mode none --n-gpu-layers all --ctx-size 12288 --flash-attn on --parallel 1 --threads 2 --threads-batch 4 --batch-size 2048 --ubatch-size 512 --cont-batching --jinja --reasoning off --cache-ram 0 --metrics --no-webui 40 + ''; 41 + Restart = "on-failure"; 42 + RestartSec = "5s"; 43 + TimeoutStartSec = "infinity"; 44 + LimitNOFILE = 1048576; 45 + }; 46 + }; 47 + }; 48 + }
+79
hosts/focalor/modules/network.nix
··· 1 + { 2 + networking = { 3 + hostName = "focalor"; 4 + hostId = "84bdc587"; 5 + useDHCP = false; 6 + nameservers = [ 7 + "10.0.0.210" 8 + "1.1.1.1" 9 + ]; 10 + firewall = { 11 + enable = true; 12 + trustedInterfaces = [ "tailscale0" ]; 13 + allowedTCPPorts = [ 14 + 22 15 + 3002 16 + ]; 17 + }; 18 + networkmanager = { 19 + enable = true; 20 + unmanaged = [ 21 + "interface-name:enp5s0" 22 + "interface-name:br0" 23 + ]; 24 + }; 25 + }; 26 + 27 + systemd.network = { 28 + enable = true; 29 + wait-online.extraArgs = [ "--interface=enp4s0" ]; 30 + netdevs.br0.netdevConfig = { 31 + Name = "br0"; 32 + Kind = "bridge"; 33 + }; 34 + networks = { 35 + "10-lan" = { 36 + matchConfig.Name = [ 37 + "enp5s0" 38 + "vm-*" 39 + ]; 40 + networkConfig.Bridge = "br0"; 41 + }; 42 + "10-lan-bridge" = { 43 + matchConfig.Name = "br0"; 44 + networkConfig = { 45 + Address = [ "10.0.0.13/24" ]; 46 + Gateway = "10.0.0.1"; 47 + DNS = [ 48 + "10.0.0.210" 49 + "1.1.1.1" 50 + ]; 51 + IPv6AcceptRA = true; 52 + }; 53 + linkConfig.RequiredForOnline = "routable"; 54 + }; 55 + }; 56 + }; 57 + 58 + services = { 59 + openssh.enable = true; 60 + printing.enable = true; 61 + tailscale = { 62 + enable = true; 63 + useRoutingFeatures = "both"; 64 + extraUpFlags = [ "--login-server=https://vpn.klbr.net" ]; 65 + }; 66 + resolved = { 67 + enable = true; 68 + settings.Resolve = { 69 + DNSSEC = "true"; 70 + Domains = [ "~." ]; 71 + FallbackDNS = [ 72 + "10.0.0.210" 73 + "1.0.0.1#one.one.one.one" 74 + ]; 75 + DNSOverTLS = "true"; 76 + }; 77 + }; 78 + }; 79 + }
+61
hosts/focalor/modules/services.nix
··· 1 + { pkgs, ... }: 2 + { 3 + services = { 4 + syncthing = { 5 + enable = true; 6 + openDefaultPorts = true; 7 + user = "regent"; 8 + dataDir = "/home/regent"; 9 + configDir = "/home/regent/.config/syncthing"; 10 + }; 11 + vscode-server = { 12 + enable = true; 13 + nodejsPackage = pkgs.nodejs_24; 14 + }; 15 + udev.extraRules = '' 16 + KERNEL=="hidraw*", ATTRS{idVendor}=="1b1c", MODE="0666" 17 + ''; 18 + }; 19 + 20 + systemd.services = { 21 + custom-fan-control = { 22 + description = "Custom Ryzen CPU Fan Control Daemon"; 23 + wantedBy = [ "multi-user.target" ]; 24 + after = [ "multi-user.target" ]; 25 + serviceConfig = { 26 + Type = "simple"; 27 + ExecStart = "${pkgs.python3}/bin/python -u /home/regent/Developer/fan_control.py"; 28 + Restart = "always"; 29 + RestartSec = 5; 30 + }; 31 + }; 32 + osu-keysounds = { 33 + description = "osu! Typing Sounds Daemon"; 34 + wantedBy = [ "multi-user.target" ]; 35 + after = [ 36 + "sound.target" 37 + "pipewire.service" 38 + ]; 39 + serviceConfig = { 40 + Type = "simple"; 41 + User = "regent"; 42 + SupplementaryGroups = [ "input" ]; 43 + Environment = [ 44 + "XDG_RUNTIME_DIR=/run/user/1000" 45 + "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus" 46 + "HOME=/home/regent" 47 + "OSUCLACK_VOLUME=1.0" 48 + ]; 49 + WorkingDirectory = "/home/regent/Developer"; 50 + ExecStart = "/home/regent/Developer/osuclack"; 51 + Restart = "always"; 52 + RestartSec = 3; 53 + }; 54 + }; 55 + }; 56 + 57 + virtualisation.docker = { 58 + enable = true; 59 + enableOnBoot = true; 60 + }; 61 + }
+64
hosts/valefar/boot-resilience.nix
··· 1 + # Keep valefar reachable over the network through a bad boot: it lives 2 + # headless, so a boot that stalls at a local console is a boot we cannot fix. 3 + { config, lib, ... }: 4 + let 5 + lanMac = "54:fb:66:01:74:ca"; 6 + in 7 + { 8 + # Vaultwarden, sonarr, prowlarr, radarr and several host-network containers 9 + # bind 100.64.0.11, which only exists once tailscaled is up. Without this 10 + # they fail with EADDRNOTAVAIL at boot and vaultwarden hits its start limit. 11 + boot.kernel.sysctl = { 12 + "net.ipv4.ip_nonlocal_bind" = 1; 13 + "net.ipv6.ip_nonlocal_bind" = 1; 14 + }; 15 + 16 + # These write under /storage. Order them after zfs-mount and refuse to 17 + # start if /storage is not actually mounted, rather than writing into the 18 + # root filesystem underneath the mountpoint. 19 + systemd.services = 20 + lib.genAttrs 21 + [ 22 + "docker" 23 + "jellyfin" 24 + "sonarr" 25 + "radarr" 26 + "container@pia-qbittorrent" 27 + "radio" 28 + ] 29 + (_: { 30 + after = [ "zfs-mount.service" ]; 31 + requires = [ "zfs-mount.service" ]; 32 + unitConfig.AssertPathIsMountPoint = "/storage"; 33 + }); 34 + 35 + # A failed fstab mount would otherwise stop in emergency.target with no 36 + # network. Carry on to multi-user so sshd and tailscale come up. 37 + systemd.enableEmergencyMode = false; 38 + 39 + # Stage-1 SSH on the LAN (port 2222, root, regent's keys) for boots that 40 + # stall before switch-root. Host key is generated once on the host: 41 + # ssh-keygen -t ed25519 -N "" -f /etc/secrets/initrd/ssh_host_ed25519_key 42 + boot.initrd.systemd.enable = true; 43 + boot.initrd.availableKernelModules = [ "r8169" ]; 44 + boot.initrd.network = { 45 + enable = true; 46 + # Drop the stage-1 address so stage 2 can enslave the NIC to vmbr0 cleanly. 47 + flushBeforeStage2 = true; 48 + ssh = { 49 + enable = true; 50 + port = 2222; 51 + hostKeys = [ "/etc/secrets/initrd/ssh_host_ed25519_key" ]; 52 + authorizedKeys = config.users.users.regent.openssh.authorizedKeys.keys; 53 + }; 54 + }; 55 + boot.initrd.systemd.network = { 56 + enable = true; 57 + networks."10-lan" = { 58 + matchConfig.PermanentMACAddress = lanMac; 59 + address = [ "10.0.0.30/24" ]; 60 + gateway = [ "10.0.0.1" ]; 61 + linkConfig.RequiredForOnline = "routable"; 62 + }; 63 + }; 64 + }
+430
hosts/valefar/default.nix
··· 1 + { 2 + config, 3 + lib, 4 + pkgs, 5 + inputs, 6 + ... 7 + }: 8 + { 9 + imports = [ 10 + "${inputs.agenix}/modules/age.nix" 11 + "${inputs.home}/nixos" 12 + inputs.proxmox-nixos.nixosModules.proxmox-ve 13 + ../../modules 14 + ../../users/regent 15 + ./hardware.nix 16 + ./secrets.nix 17 + ./boot-resilience.nix 18 + ./nat-guard.nix 19 + ./pia-qbittorrent.nix 20 + ./pia-exit.nix 21 + ./wg-mesh.nix 22 + ]; 23 + 24 + services.proxmox-ve = { 25 + enable = true; 26 + ipAddress = "10.0.0.30"; 27 + bridges = [ "vmbr0" ]; 28 + }; 29 + 30 + nix.gc = { 31 + automatic = lib.mkForce true; 32 + dates = "weekly"; 33 + options = "--delete-older-than 14d"; 34 + }; 35 + nix.settings = { 36 + auto-optimise-store = true; 37 + trusted-users = lib.mkForce [ 38 + "root" 39 + "regent" 40 + ]; 41 + substituters = [ "https://cache.saumon.network/proxmox-nixos" ]; 42 + trusted-public-keys = [ "proxmox-nixos:D9RYSWpQQC/msZUWphOY2I5RLH5Dd6yQcaHIuug7dWM=" ]; 43 + }; 44 + services.journald.settings.Journal.SystemMaxUse = "1G"; 45 + boot.kernel.sysctl."net.ipv4.ip_forward" = 1; 46 + time.timeZone = "America/New_York"; 47 + i18n.defaultLocale = "en_US.UTF-8"; 48 + programs.nix-ld.enable = true; 49 + services.openssh.enable = true; 50 + services.tailscale = { 51 + enable = true; 52 + useRoutingFeatures = "both"; 53 + extraUpFlags = [ "--login-server=https://vpn.klbr.net" ]; 54 + }; 55 + boot.loader = { 56 + systemd-boot.enable = true; 57 + efi.canTouchEfiVariables = true; 58 + }; 59 + fileSystems."/boot".options = [ "umask=0077" ]; 60 + hardware.graphics.enable = true; 61 + services.xserver.videoDrivers = [ "nvidia" ]; 62 + hardware.nvidia = { 63 + modesetting.enable = true; 64 + powerManagement.enable = false; 65 + powerManagement.finegrained = false; 66 + open = true; 67 + nvidiaSettings = true; 68 + package = config.boot.kernelPackages.nvidiaPackages.latest; 69 + }; 70 + environment.variables = { 71 + GBM_BACKEND = "nvidia-drm"; 72 + __GLX_VENDOR_LIBRARY_NAME = "nvidia"; 73 + }; 74 + 75 + services.printing.enable = true; 76 + 77 + services.searx = { 78 + enable = true; 79 + environmentFile = config.age.secrets."searx.env".path; 80 + redisCreateLocally = false; 81 + settings = { 82 + use_default_settings = true; 83 + server = { 84 + secret_key = "$SEARX_SECRET_KEY"; 85 + port = 8888; 86 + bind_address = "0.0.0.0"; 87 + limiter = false; 88 + image_proxy = true; 89 + base_url = "http://valefar:8888/"; 90 + }; 91 + engines = 92 + map 93 + (name: { 94 + inherit name; 95 + disabled = false; 96 + }) 97 + [ 98 + "bing" 99 + "duckduckgo" 100 + "brave" 101 + "startpage" 102 + "qwant" 103 + "mojeek" 104 + "marginalia" 105 + "wikipedia" 106 + ] 107 + ++ [ 108 + { 109 + name = "google"; 110 + disabled = false; 111 + use_mobile_ui = true; 112 + } 113 + ]; 114 + search = { 115 + safe_search = 0; 116 + formats = [ 117 + "html" 118 + "json" 119 + ]; 120 + }; 121 + }; 122 + }; 123 + 124 + services.pocket-id = { 125 + enable = true; 126 + dataDir = "/var/lib/pocket-id"; 127 + credentials = { 128 + ENCRYPTION_KEY = config.age.secrets."pocket-id-encryption-key".path; 129 + MAXMIND_LICENSE_KEY = config.age.secrets."pocket-id-maxmind-license-key".path; 130 + }; 131 + settings = { 132 + APP_URL = "https://pocketid.nekomimi.pet"; 133 + DB_CONNECTION_STRING = "pocket-id.db"; 134 + GEOLITE_DB_PATH = "GeoLite2-City.mmdb"; 135 + PORT = 3000; 136 + TRUST_PROXY = true; 137 + UPLOAD_PATH = "uploads"; 138 + }; 139 + }; 140 + 141 + networking = { 142 + useNetworkd = true; 143 + useDHCP = false; 144 + hostName = "valefar"; 145 + hostId = "2a07da90"; 146 + firewall.enable = false; 147 + }; 148 + systemd.network = { 149 + enable = true; 150 + links."10-lan" = { 151 + matchConfig.PermanentMACAddress = "54:fb:66:01:74:ca"; 152 + linkConfig = { 153 + NamePolicy = "keep kernel database onboard slot path"; 154 + AlternativeNamesPolicy = "database onboard slot path"; 155 + MACAddressPolicy = "persistent"; 156 + WakeOnLan = "magic"; 157 + }; 158 + }; 159 + networks = { 160 + "10-lan" = { 161 + matchConfig.PermanentMACAddress = "54:fb:66:01:74:ca"; 162 + networkConfig = { 163 + Bridge = "vmbr0"; 164 + DHCP = "no"; 165 + LinkLocalAddressing = "no"; 166 + IPv6AcceptRA = false; 167 + }; 168 + }; 169 + "11-lan-by-name" = { 170 + matchConfig.Name = "enp5s0"; 171 + networkConfig = { 172 + Bridge = "vmbr0"; 173 + DHCP = "no"; 174 + LinkLocalAddressing = "no"; 175 + IPv6AcceptRA = false; 176 + }; 177 + }; 178 + "10-lan-bridge" = { 179 + matchConfig.Name = "vmbr0"; 180 + networkConfig = { 181 + Address = [ 182 + "10.0.0.30/24" 183 + "2601:5c2:8400:26c0::30/64" 184 + ]; 185 + Gateway = "10.0.0.1"; 186 + DNS = [ 187 + "10.0.0.210" 188 + "1.1.1.1" 189 + "1.0.0.1" 190 + ]; 191 + IPv6AcceptRA = true; 192 + }; 193 + routes = [ 194 + { 195 + Destination = "0.0.0.0/0"; 196 + Gateway = "10.0.0.1"; 197 + } 198 + ]; 199 + linkConfig.RequiredForOnline = "routable"; 200 + }; 201 + }; 202 + netdevs.br0.netdevConfig = { 203 + Name = "vmbr0"; 204 + Kind = "bridge"; 205 + }; 206 + }; 207 + services.resolved = { 208 + enable = true; 209 + settings.Resolve = { 210 + DNSSEC = "false"; 211 + Domains = [ "~." ]; 212 + FallbackDNS = [ 213 + "10.0.0.210" 214 + "1.1.1.1" 215 + ]; 216 + DNSOverTLS = "false"; 217 + }; 218 + }; 219 + 220 + boot = { 221 + supportedFilesystems = [ "zfs" ]; 222 + kernelModules = [ 223 + "nct6775" 224 + "coretemp" 225 + ]; 226 + zfs = { 227 + extraPools = [ "storage" ]; 228 + devNodes = "/dev/disk/by-id"; 229 + forceImportAll = true; 230 + forceImportRoot = true; 231 + }; 232 + }; 233 + systemd.services.zfs-import-cache.enable = false; 234 + services.zfs = { 235 + autoScrub.enable = true; 236 + trim.enable = true; 237 + }; 238 + 239 + services.jellyfin = { 240 + enable = true; 241 + dataDir = "/storage/jellyfin"; 242 + }; 243 + services.prowlarr = { 244 + enable = true; 245 + settings.server = { 246 + bindaddress = "100.64.0.11"; 247 + port = 9696; 248 + }; 249 + }; 250 + services.sonarr = { 251 + enable = true; 252 + dataDir = "/storage/sonarr"; 253 + settings = { 254 + server = { 255 + bindAddress = "100.64.0.11"; 256 + port = 8989; 257 + }; 258 + update = { 259 + automatically = false; 260 + mechanism = "external"; 261 + }; 262 + log.analyticsEnabled = false; 263 + }; 264 + }; 265 + services.radarr = { 266 + enable = true; 267 + dataDir = "/storage/radarr"; 268 + settings = { 269 + server = { 270 + bindAddress = "100.64.0.11"; 271 + port = 7878; 272 + }; 273 + update = { 274 + automatically = false; 275 + mechanism = "external"; 276 + }; 277 + log.analyticsEnabled = false; 278 + }; 279 + }; 280 + systemd.services.radarr.serviceConfig.PrivateUsers = lib.mkForce false; 281 + systemd.services.sonarr.serviceConfig.PrivateUsers = lib.mkForce false; 282 + users.users.radarr = { 283 + isSystemUser = true; 284 + group = "radarr"; 285 + home = "/storage/radarr"; 286 + uid = config.ids.uids.radarr; 287 + extraGroups = [ "jellyfin" ]; 288 + }; 289 + users.groups.radarr.gid = config.ids.gids.radarr; 290 + users.users.sonarr = { 291 + isSystemUser = true; 292 + group = "sonarr"; 293 + home = "/storage/sonarr"; 294 + uid = config.ids.uids.sonarr; 295 + extraGroups = [ "jellyfin" ]; 296 + }; 297 + users.groups.sonarr.gid = config.ids.gids.sonarr; 298 + services.vaultwarden = { 299 + enable = true; 300 + config = { 301 + DOMAIN = "https://vault.nekomimi.pet"; 302 + ROCKET_ADDRESS = "100.64.0.11"; 303 + ROCKET_PORT = 8222; 304 + SIGNUPS_ALLOWED = false; 305 + SSO_ENABLED = true; 306 + SSO_ONLY = true; 307 + SSO_PKCE = true; 308 + SSO_SCOPES = "email profile groups offline_access"; 309 + SSO_AUTHORITY = "https://pocketid.nekomimi.pet"; 310 + }; 311 + environmentFile = config.age.secrets."vaultwarden-oidc.env".path; 312 + }; 313 + 314 + systemd.tmpfiles.rules = [ 315 + "d /storage/tm_share 0755 regent users" 316 + "d /storage/media 0755 jellyfin jellyfin -" 317 + "d /storage/media/.incoming 2775 jellyfin jellyfin -" 318 + "d /storage/media/tv 2775 jellyfin jellyfin -" 319 + ]; 320 + services.samba = { 321 + enable = true; 322 + settings = { 323 + global = { 324 + "workgroup" = "WORKGROUP"; 325 + "server string" = "valefar"; 326 + "netbios name" = "valefar"; 327 + "security" = "user"; 328 + "hosts allow" = "100.64.0.0/10 10.0.0.0/24 127.0.0.1 localhost"; 329 + "hosts deny" = "0.0.0.0/0"; 330 + "guest account" = "nobody"; 331 + "map to guest" = "bad user"; 332 + }; 333 + tm_share = { 334 + path = "/storage/tm_share"; 335 + "valid users" = "regent"; 336 + public = "yes"; 337 + writeable = "yes"; 338 + "force user" = "regent"; 339 + "fruit:aapl" = "yes"; 340 + "fruit:time machine" = "yes"; 341 + "vfs objects" = "catia fruit streams_xattr"; 342 + }; 343 + }; 344 + }; 345 + services.netatalk = { 346 + enable = true; 347 + settings.time-machine = { 348 + path = "/storage/timemachine"; 349 + "valid users" = "regent"; 350 + "time machine" = true; 351 + }; 352 + }; 353 + services.avahi = { 354 + enable = true; 355 + nssmdns4 = true; 356 + publish = { 357 + enable = true; 358 + userServices = true; 359 + }; 360 + extraServiceFiles.timemachine = '' 361 + <?xml version="1.0" standalone='no'?> 362 + <!DOCTYPE service-group SYSTEM "avahi-service.dtd"> 363 + <service-group> 364 + <name replace-wildcards="yes">%h</name> 365 + <service> 366 + <type>_smb._tcp</type> 367 + <port>445</port> 368 + </service> 369 + <service> 370 + <type>_device-info._tcp</type> 371 + <port>0</port> 372 + <txt-record>model=TimeCapsule8,119</txt-record> 373 + </service> 374 + <service> 375 + <type>_adisk._tcp</type> 376 + <txt-record>dk0=adVN=tm_share,adVF=0x82</txt-record> 377 + <txt-record>sys=waMa=0,adVF=0x100</txt-record> 378 + </service> 379 + </service-group> 380 + ''; 381 + }; 382 + 383 + users.users.niri = { 384 + isSystemUser = true; 385 + uid = 988; 386 + group = "users"; 387 + shell = pkgs.bashInteractive; 388 + extraGroups = [ "wheel" ]; 389 + openssh.authorizedKeys.keys = [ 390 + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ7Y9Je7H3gC72cgdEH4wifUDsmhKMeU5Z4oL1s1WcSE niri@nekomimi.pet" 391 + ]; 392 + }; 393 + systemd.services.radio = { 394 + description = "faint signal fm"; 395 + wantedBy = [ "multi-user.target" ]; 396 + wants = [ "network-online.target" ]; 397 + after = [ "network-online.target" ]; 398 + environment.NO_COLOR = "1"; 399 + path = [ 400 + pkgs.ffmpeg 401 + pkgs.yt-dlp 402 + ]; 403 + serviceConfig = { 404 + User = "regent"; 405 + Group = "users"; 406 + WorkingDirectory = "/home/regent/radio"; 407 + ExecStart = "/home/regent/radio/target/release/radio"; 408 + Restart = "always"; 409 + RestartSec = "5s"; 410 + }; 411 + }; 412 + services.syncthing = { 413 + guiAddress = "0.0.0.0:8384"; 414 + enable = true; 415 + }; 416 + virtualisation.docker = { 417 + enable = true; 418 + enableOnBoot = true; 419 + }; 420 + environment.systemPackages = with pkgs; [ 421 + code-server 422 + ffmpeg 423 + yt-dlp 424 + nodejs 425 + python3 426 + smartmontools 427 + ]; 428 + 429 + system.stateVersion = "24.11"; 430 + }
+44
hosts/valefar/hardware.nix
··· 1 + { 2 + config, 3 + lib, 4 + modulesPath, 5 + ... 6 + }: 7 + 8 + { 9 + imports = [ 10 + (modulesPath + "/installer/scan/not-detected.nix") 11 + ]; 12 + 13 + boot.initrd.availableKernelModules = [ 14 + "xhci_pci" 15 + "ahci" 16 + "mpt3sas" 17 + "nvme" 18 + "usbhid" 19 + "uas" 20 + "sd_mod" 21 + ]; 22 + boot.kernelModules = [ "kvm-amd" ]; 23 + 24 + fileSystems."/" = { 25 + device = "/dev/disk/by-uuid/e02d1d07-3bc8-4d1d-a301-6d589f4b4b6d"; 26 + fsType = "ext4"; 27 + }; 28 + 29 + fileSystems."/boot" = { 30 + device = "/dev/disk/by-uuid/B3DE-0187"; 31 + fsType = "vfat"; 32 + options = [ 33 + "fmask=0022" 34 + "dmask=0022" 35 + ]; 36 + }; 37 + 38 + swapDevices = [ 39 + { device = "/dev/disk/by-uuid/c8f24f31-49e0-486c-9f63-1d31b2e36ce9"; } 40 + ]; 41 + 42 + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 43 + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; 44 + }
+107
hosts/valefar/nat-guard.nix
··· 1 + { config, lib, ... }: 2 + let 3 + nat = config.networking.nat; 4 + outgoing = lib.optionals (nat.externalInterface != null) [ 5 + "-o" 6 + nat.externalInterface 7 + ]; 8 + translation = 9 + if nat.externalIP == null then 10 + [ 11 + "-j" 12 + "MASQUERADE" 13 + ] 14 + else 15 + [ 16 + "-j" 17 + "SNAT" 18 + "--to-source" 19 + nat.externalIP 20 + ]; 21 + in 22 + { 23 + config = 24 + lib.mkIf (nat.enable && !config.networking.firewall.enable && !config.networking.nftables.enable) 25 + { 26 + assertions = [ 27 + { 28 + assertion = lib.all (name: builtins.stringLength name < 16) ( 29 + nat.internalInterfaces ++ lib.optional (nat.externalInterface != null) nat.externalInterface 30 + ); 31 + message = "valefar NAT interface names must be at most 15 bytes; use internalIPs for long container names."; 32 + } 33 + ]; 34 + 35 + systemd.services.nat = { 36 + wantedBy = [ "multi-user.target" ]; 37 + # Restart in the new generation, not stop before activation then start later. 38 + stopIfChanged = false; 39 + serviceConfig = { 40 + Restart = "on-failure"; 41 + RestartSec = "5s"; 42 + }; 43 + }; 44 + 45 + # A successful oneshot can be stopped or retain stale kernel rules without 46 + # becoming failed. Check the live rules against the merged NAT configuration. 47 + systemd.services.nat-healthcheck = { 48 + description = "Check and repair container NAT"; 49 + after = [ "nat.service" ]; 50 + path = [ 51 + config.networking.firewall.package 52 + config.systemd.package 53 + ]; 54 + serviceConfig = { 55 + Type = "oneshot"; 56 + TimeoutStartSec = "60s"; 57 + }; 58 + enableStrictShellChecks = true; 59 + script = '' 60 + check_nat() { 61 + systemctl is-active --quiet nat.service || return 1 62 + iptables -w 5 -t nat -C POSTROUTING -j nixos-nat-post || return 1 63 + iptables -w 5 -t filter -C FORWARD -j nixos-filter-forward || return 1 64 + ${lib.concatMapStringsSep "\n" (subnet: '' 65 + iptables -w 5 -t nat -C nixos-nat-post ${ 66 + lib.escapeShellArgs ( 67 + [ 68 + "-s" 69 + subnet 70 + ] 71 + ++ outgoing 72 + ++ translation 73 + ) 74 + } || return 1 75 + iptables -w 5 -t filter -C nixos-filter-forward ${ 76 + lib.escapeShellArgs ( 77 + [ 78 + "-s" 79 + subnet 80 + ] 81 + ++ outgoing 82 + ++ [ 83 + "-j" 84 + "ACCEPT" 85 + ] 86 + ) 87 + } || return 1 88 + '') nat.internalIPs} 89 + } 90 + 91 + if ! check_nat; then 92 + echo "container NAT is inactive or incomplete; restarting nat.service" >&2 93 + systemctl restart nat.service 94 + check_nat 95 + fi 96 + ''; 97 + }; 98 + systemd.timers.nat-healthcheck = { 99 + wantedBy = [ "timers.target" ]; 100 + timerConfig = { 101 + OnBootSec = "30s"; 102 + OnUnitInactiveSec = "60s"; 103 + AccuracySec = "1s"; 104 + }; 105 + }; 106 + }; 107 + }
+297
hosts/valefar/pia-exit.nix
··· 1 + { 2 + config, 3 + lib, 4 + pkgs, 5 + ... 6 + }: 7 + let 8 + wgAuth = config.age.secrets."pia-wireguard-auth.env".path; 9 + piaSource = ./pia-manual; 10 + # PIA rotates and repurposes endpoint fleets without notice. 2026-09-21: 11 + # the old WireGuard endpoint was retired and OpenVPN tcp/443 adopted. 12 + # 2026-09-24: the entire 45.88.217.x DC fleet vanished and its :443 began 13 + # answering as a plain nginx front, so openvpn looped on "Bad encapsulated 14 + # packet length" (it was reading ASCII "HT" from an HTTP response). 15 + # Rotation recipe: fetch https://serverlist.piaservers.net/vpninfo/servers/v6 16 + # (JSON is line 1), pick an "ovpntcp" server, verify it handshakes, change 17 + # `endpoint` here and in pia-qbittorrent.nix, rebuild. 18 + endpoint = "206.206.95.51"; # us-washingtondc / ovpntcp 19 + gateway = "192.168.173.1"; 20 + headscale = "94.237.26.47"; # vpn.klbr.net 21 + # One-time Tailnet enrollment bootstrap. Kept mounted so a wiped node state 22 + # re-enrolls itself. Read-only; the autoconnect script only reads it when the 23 + # node has no valid state, so it is not consulted on a healthy boot. 24 + authKey = "/home/regent/.pia-exit-authkey"; 25 + # Host-side bootstrap: token + OpenVPN config + credentials land under 26 + # /run/pia-exit and are bind-mounted read-only into the guest. The container 27 + # never needs clear-net DNS or HTTPS. 28 + prepare = pkgs.writeShellScript "pia-exit-openvpn-prepare" '' 29 + set -euo pipefail 30 + umask 077 31 + export PATH=${ 32 + lib.makeBinPath [ 33 + pkgs.bash 34 + pkgs.coreutils 35 + pkgs.curl 36 + pkgs.jq 37 + ] 38 + } 39 + set -a 40 + . ${wgAuth} 41 + set +a 42 + token=$(curl --fail --silent --show-error --max-time 30 \ 43 + --form "username=$PIA_USER" --form "password=$PIA_PASS" \ 44 + https://www.privateinternetaccess.com/api/client/v2/token \ 45 + | jq -er '.token | strings | select(length > 0)') 46 + # OpenVPN token auth: username is the first 62 chars, password the rest. 47 + printf '%s\n%s\n' "''${token:0:62}" "''${token:62}" > /run/pia-exit/pia-creds 48 + { 49 + echo "client" 50 + echo "dev pia" 51 + echo "dev-type tun" 52 + echo "proto tcp" 53 + echo "remote ${endpoint} 443" 54 + echo "resolv-retry infinite" 55 + echo "nobind" 56 + echo "persist-key" 57 + echo "persist-tun" 58 + echo "remote-cert-tls server" 59 + echo "redirect-gateway def1" 60 + echo "reneg-sec 0" 61 + echo "verb 1" 62 + echo "auth-user-pass /etc/openvpn/pia-creds" 63 + echo "<ca>" 64 + cat ${piaSource}/ca.rsa.4096.crt 65 + echo "</ca>" 66 + } > /run/pia-exit/pia.ovpn 67 + ''; 68 + in 69 + { 70 + # Host fetches PIA bootstrap material; the container never needs clear-net DNS/HTTPS. 71 + # 72 + # NixOS adds the host-side gateway and brings the veth up in ITS OWN postStart, 73 + # after guest readiness. So the tunnel must NOT live in the guest's boot path: 74 + # start it from here instead, after the host side exists. 75 + systemd.services."container@pia-exit" = { 76 + wants = [ "network-online.target" ]; 77 + after = [ "network-online.target" ]; 78 + preStart = lib.mkBefore "${prepare}"; 79 + postStart = lib.mkAfter '' 80 + ${config.systemd.package}/bin/systemctl -M pia-exit start --no-block tailscaled-autoconnect.service 81 + ${config.systemd.package}/bin/systemctl -M pia-exit start --no-block pia-openvpn.service 82 + ''; 83 + serviceConfig.RuntimeDirectory = "pia-exit"; 84 + serviceConfig.RuntimeDirectoryMode = "0700"; 85 + serviceConfig.TimeoutStartSec = lib.mkForce "4min"; 86 + }; 87 + # NixOS owns this veth; stop networkd's generic container DHCP/NAT handling. 88 + systemd.network.networks."40-pia-exit" = { 89 + matchConfig.Name = "ve-pia-exit"; 90 + linkConfig.Unmanaged = true; 91 + }; 92 + containers.pia-exit = { 93 + autoStart = true; 94 + privateNetwork = true; 95 + enableTun = true; 96 + hostAddress = gateway; 97 + localAddress = "192.168.173.2"; 98 + bindMounts = { 99 + "/etc/openvpn/pia.ovpn" = { 100 + hostPath = "/run/pia-exit/pia.ovpn"; 101 + isReadOnly = true; 102 + }; 103 + "/etc/openvpn/pia-creds" = { 104 + hostPath = "/run/pia-exit/pia-creds"; 105 + isReadOnly = true; 106 + }; 107 + # Enrollment key, read-only. Kept so a wiped node state can re-enroll itself. 108 + "/run/tailscale-authkey" = { 109 + hostPath = authKey; 110 + isReadOnly = true; 111 + }; 112 + }; 113 + config = 114 + { 115 + config, 116 + lib, 117 + pkgs, 118 + ... 119 + }: 120 + { 121 + networking.useDHCP = false; 122 + networking.enableIPv6 = false; 123 + # Static resolver: resolvconf would otherwise inherit the host's 127.0.0.53 124 + # stub, and systemd-resolved is not running in here. 125 + networking.nameservers = [ "1.1.1.1" ]; 126 + networking.resolvconf.enable = false; 127 + environment.etc."resolv.conf".text = "nameserver 1.1.1.1\n"; 128 + # Pin the coordination server so enrollment never depends on DNS at all. 129 + networking.hosts."${headscale}" = [ "vpn.klbr.net" ]; 130 + networking.interfaces.eth0.ipv4.routes = [ 131 + { 132 + address = endpoint; 133 + prefixLength = 32; 134 + via = gateway; 135 + } 136 + ]; 137 + environment.systemPackages = with pkgs; [ 138 + bash 139 + coreutils 140 + curl 141 + gnugrep 142 + gnused 143 + jq 144 + openvpn 145 + iproute2 146 + ]; 147 + boot.kernel.sysctl."net.ipv4.ip_forward" = 1; 148 + networking.firewall = { 149 + enable = true; 150 + checkReversePath = "loose"; 151 + allowedUDPPorts = [ 41641 ]; 152 + # Default-deny OUTPUT. Everything leaves through the tunnel, except a 153 + # narrow control plane so the node can enroll and stay reachable. 154 + extraCommands = '' 155 + ${pkgs.iptables}/bin/iptables-restore --noflush <<'RULES' 156 + *filter 157 + :OUTPUT DROP [0:0] 158 + :FORWARD DROP [0:0] 159 + -F OUTPUT 160 + -F FORWARD 161 + -A OUTPUT -o lo -j ACCEPT 162 + -A OUTPUT -o pia -j ACCEPT 163 + -A OUTPUT -o tailscale0 -j ACCEPT 164 + -A OUTPUT -o eth0 -d ${endpoint}/32 -p tcp --dport 443 -j ACCEPT 165 + -A OUTPUT -o eth0 -d ${headscale}/32 -p tcp --dport 443 -j ACCEPT 166 + -A OUTPUT -o eth0 -d ${headscale}/32 -p udp --dport 3478 -j ACCEPT 167 + -A OUTPUT -o eth0 -d 1.1.1.1/32 -p udp --dport 53 -j ACCEPT 168 + -A OUTPUT -o eth0 -d 1.1.1.1/32 -p tcp --dport 53 -j ACCEPT 169 + -A OUTPUT -o eth0 -d ${gateway}/32 -j ACCEPT 170 + -A FORWARD -i tailscale0 -o pia -j ACCEPT 171 + -A FORWARD -i pia -o tailscale0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT 172 + COMMIT 173 + *nat 174 + :POSTROUTING ACCEPT [0:0] 175 + -A POSTROUTING -o pia -s 100.64.0.0/10 -j MASQUERADE 176 + COMMIT 177 + RULES 178 + ${pkgs.iptables}/bin/ip6tables -P OUTPUT DROP 179 + ${pkgs.iptables}/bin/ip6tables -P FORWARD DROP 180 + ''; 181 + }; 182 + # Enrollment also needs the host-side gateway, which NixOS only wires after 183 + # guest readiness. Keep it out of the boot transaction; the host starts it. 184 + systemd.services.tailscaled-autoconnect.wantedBy = lib.mkForce [ ]; 185 + services.tailscale = { 186 + enable = true; 187 + useRoutingFeatures = "server"; 188 + authKeyFile = "/run/tailscale-authkey"; 189 + extraUpFlags = [ 190 + "--login-server=https://vpn.klbr.net" 191 + "--hostname=pia-exit" 192 + "--accept-dns=false" 193 + "--netfilter-mode=off" 194 + "--advertise-exit-node" 195 + ]; 196 + }; 197 + # Deliberately NOT wantedBy multi-user.target: the host veth is not wired 198 + # until after guest readiness. Started from the host postStart instead. 199 + systemd.services.pia-openvpn = { 200 + requires = [ "firewall.service" ]; 201 + after = [ 202 + "network-online.target" 203 + "firewall.service" 204 + ]; 205 + wants = [ "network-online.target" ]; 206 + path = [ 207 + pkgs.curl 208 + pkgs.iproute2 209 + pkgs.gnugrep 210 + ]; 211 + serviceConfig = { 212 + Type = "simple"; 213 + Restart = "always"; 214 + RestartSec = "15s"; 215 + # The endpoint must leave via eth0; the static pin is applied by 216 + # networkd at guest boot. Wait it out rather than race it. 217 + ExecStartPre = pkgs.writeShellScript "pia-openvpn-pin" '' 218 + for i in $(seq 1 30); do 219 + if ip -4 route get ${endpoint} 2>/dev/null | grep -q " dev eth0"; then 220 + exit 0 221 + fi 222 + sleep 1 223 + done 224 + exit 1 225 + ''; 226 + ExecStart = "${pkgs.openvpn}/bin/openvpn --config /etc/openvpn/pia.ovpn"; 227 + }; 228 + # Fail closed: the unit only counts as up once the tunnel really 229 + # carries traffic. A failing probe restarts openvpn (Restart=always). 230 + postStart = '' 231 + ok=0 232 + for i in $(seq 1 30); do 233 + if curl --fail --silent --max-time 5 --interface pia \ 234 + https://1.1.1.1/cdn-cgi/trace | grep -q '^ip='; then 235 + ok=1 236 + break 237 + fi 238 + sleep 1 239 + done 240 + [ "$ok" = 1 ] || exit 1 241 + systemctl start --no-block pia-tailscale-kick.service 242 + ''; 243 + }; 244 + # tailscaled holds one long-lived control connection to the headscale 245 + # server. When the tunnel's default route flips (endpoint rotation or 246 + # reconnect), that TCP conn silently dies and the backend wedges in 247 + # NoState forever. Give autoconnect its full 90s window first, then 248 + # restart tailscaled once if the backend did not reach Running. 249 + systemd.services.pia-tailscale-kick = { 250 + description = "Restart tailscaled when its control connection goes stale across a tunnel flip"; 251 + after = [ 252 + "pia-openvpn.service" 253 + "tailscaled.service" 254 + ]; 255 + path = [ 256 + pkgs.tailscale 257 + pkgs.jq 258 + ]; 259 + serviceConfig = { 260 + Type = "oneshot"; 261 + }; 262 + script = '' 263 + sleep 95 264 + state=$(tailscale status --json --peers=false 2>/dev/null | jq -r '.BackendState') 265 + if [ "$state" != "Running" ]; then 266 + systemctl restart tailscaled.service 267 + fi 268 + for i in $(seq 1 30); do 269 + state=$(tailscale status --json --peers=false 2>/dev/null | jq -r '.BackendState') 270 + [ "$state" = "Running" ] && exit 0 271 + sleep 2 272 + done 273 + exit 1 274 + ''; 275 + }; 276 + # Tailnet traffic must not follow the tunnel's redirect-gateway routes 277 + # (0.0.0.0/1, 128.0.0.0/1 in main); the /10 route is more specific and 278 + # wins, keeping tailscale0 reachable for exit-node clients. 279 + systemd.services.pia-exit-tailnet-route = { 280 + description = "Route tailnet traffic out tailscale0, ahead of the tunnel default"; 281 + after = [ "tailscaled.service" ]; 282 + wants = [ "tailscaled.service" ]; 283 + wantedBy = [ "multi-user.target" ]; 284 + path = [ pkgs.iproute2 ]; 285 + serviceConfig = { 286 + Type = "oneshot"; 287 + RemainAfterExit = true; 288 + }; 289 + script = '' 290 + ip route replace 100.64.0.0/10 dev tailscale0 table main 291 + ''; 292 + }; 293 + system.stateVersion = "26.05"; 294 + }; 295 + }; 296 + networking.nat.internalIPs = [ "192.168.173.0/24" ]; 297 + }
+1
hosts/valefar/pia-manual/SOURCE
··· 1 + vendor copy from pia-foss/manual-connections master, retrieved 2026-09-12; source: https://github.com/pia-foss/manual-connections
+43
hosts/valefar/pia-manual/ca.rsa.4096.crt
··· 1 + -----BEGIN CERTIFICATE----- 2 + MIIHqzCCBZOgAwIBAgIJAJ0u+vODZJntMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYD 3 + VQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNV 4 + BAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIElu 5 + dGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3Mx 6 + IDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkB 7 + FiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzQw 8 + MzNaFw0zNDA0MTIxNzQwMzNaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex 9 + EzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQg 10 + QWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UE 11 + AxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50 12 + ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVy 13 + bmV0YWNjZXNzLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALVk 14 + hjumaqBbL8aSgj6xbX1QPTfTd1qHsAZd2B97m8Vw31c/2yQgZNf5qZY0+jOIHULN 15 + De4R9TIvyBEbvnAg/OkPw8n/+ScgYOeH876VUXzjLDBnDb8DLr/+w9oVsuDeFJ9K 16 + V2UFM1OYX0SnkHnrYAN2QLF98ESK4NCSU01h5zkcgmQ+qKSfA9Ny0/UpsKPBFqsQ 17 + 25NvjDWFhCpeqCHKUJ4Be27CDbSl7lAkBuHMPHJs8f8xPgAbHRXZOxVCpayZ2SND 18 + fCwsnGWpWFoMGvdMbygngCn6jA/W1VSFOlRlfLuuGe7QFfDwA0jaLCxuWt/BgZyl 19 + p7tAzYKR8lnWmtUCPm4+BtjyVDYtDCiGBD9Z4P13RFWvJHw5aapx/5W/CuvVyI7p 20 + Kwvc2IT+KPxCUhH1XI8ca5RN3C9NoPJJf6qpg4g0rJH3aaWkoMRrYvQ+5PXXYUzj 21 + tRHImghRGd/ydERYoAZXuGSbPkm9Y/p2X8unLcW+F0xpJD98+ZI+tzSsI99Zs5wi 22 + jSUGYr9/j18KHFTMQ8n+1jauc5bCCegN27dPeKXNSZ5riXFL2XX6BkY68y58UaNz 23 + meGMiUL9BOV1iV+PMb7B7PYs7oFLjAhh0EdyvfHkrh/ZV9BEhtFa7yXp8XR0J6vz 24 + 1YV9R6DYJmLjOEbhU8N0gc3tZm4Qz39lIIG6w3FDAgMBAAGjggFUMIIBUDAdBgNV 25 + HQ4EFgQUrsRtyWJftjpdRM0+925Y6Cl08SUwggEfBgNVHSMEggEWMIIBEoAUrsRt 26 + yWJftjpdRM0+925Y6Cl08SWhge6kgeswgegxCzAJBgNVBAYTAlVTMQswCQYDVQQI 27 + EwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRl 28 + cm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAw 29 + HgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0 30 + ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRl 31 + aW50ZXJuZXRhY2Nlc3MuY29tggkAnS7684Nkme0wDAYDVR0TBAUwAwEB/zANBgkq 32 + hkiG9w0BAQ0FAAOCAgEAJsfhsPk3r8kLXLxY+v+vHzbr4ufNtqnL9/1Uuf8NrsCt 33 + pXAoyZ0YqfbkWx3NHTZ7OE9ZRhdMP/RqHQE1p4N4Sa1nZKhTKasV6KhHDqSCt/dv 34 + Em89xWm2MVA7nyzQxVlHa9AkcBaemcXEiyT19XdpiXOP4Vhs+J1R5m8zQOxZlV1G 35 + tF9vsXmJqWZpOVPmZ8f35BCsYPvv4yMewnrtAC8PFEK/bOPeYcKN50bol22QYaZu 36 + LfpkHfNiFTnfMh8sl/ablPyNY7DUNiP5DRcMdIwmfGQxR5WEQoHL3yPJ42LkB5zs 37 + 6jIm26DGNXfwura/mi105+ENH1CaROtRYwkiHb08U6qLXXJz80mWJkT90nr8Asj3 38 + 5xN2cUppg74nG3YVav/38P48T56hG1NHbYF5uOCske19F6wi9maUoto/3vEr0rnX 39 + JUp2KODmKdvBI7co245lHBABWikk8VfejQSlCtDBXn644ZMtAdoxKNfR2WTFVEwJ 40 + iyd1Fzx0yujuiXDROLhISLQDRjVVAvawrAtLZWYK31bY7KlezPlQnl/D9Asxe85l 41 + 8jO5+0LdJ6VyOs/Hd4w52alDW/MFySDZSfQHMTIc30hLBJ8OnCEIvluVQQ2UQvoW 42 + +no177N9L2Y+M9TcTA62ZyMXShHQGeh20rb4kK8f+iFX8NxtdHVSkxMEFSfDDyQ= 43 + -----END CERTIFICATE-----
+241
hosts/valefar/pia-qbittorrent.nix
··· 1 + { 2 + config, 3 + lib, 4 + pkgs, 5 + ... 6 + }: 7 + let 8 + wgAuth = config.age.secrets."pia-wireguard-auth.env".path; 9 + piaSource = ./pia-manual; 10 + # Same story as pia-exit.nix: PIA rotates endpoint fleets without notice; 11 + # rotate `endpoint` in lockstep with pia-exit.nix. 12 + endpoint = "206.206.95.51"; # us-washingtondc / ovpntcp 13 + gateway = "192.168.172.1"; 14 + # Host-side bootstrap: token + OpenVPN config + credentials under 15 + # /run/pia-qbittorrent, bind-mounted read-only into the guest. 16 + prepare = pkgs.writeShellScript "pia-qbittorrent-openvpn-prepare" '' 17 + set -euo pipefail 18 + umask 077 19 + export PATH=${ 20 + lib.makeBinPath [ 21 + pkgs.bash 22 + pkgs.coreutils 23 + pkgs.curl 24 + pkgs.jq 25 + ] 26 + } 27 + set -a 28 + . ${wgAuth} 29 + set +a 30 + token=$(curl --fail --silent --show-error --max-time 30 \ 31 + --form "username=$PIA_USER" --form "password=$PIA_PASS" \ 32 + https://www.privateinternetaccess.com/api/client/v2/token \ 33 + | jq -er '.token | strings | select(length > 0)') 34 + # OpenVPN token auth: username is the first 62 chars, password the rest. 35 + printf '%s\n%s\n' "''${token:0:62}" "''${token:62}" > /run/pia-qbittorrent/pia-creds 36 + { 37 + echo "client" 38 + echo "dev pia" 39 + echo "dev-type tun" 40 + echo "proto tcp" 41 + echo "remote ${endpoint} 443" 42 + echo "resolv-retry infinite" 43 + echo "nobind" 44 + echo "persist-key" 45 + echo "persist-tun" 46 + echo "remote-cert-tls server" 47 + echo "redirect-gateway def1" 48 + echo "reneg-sec 0" 49 + echo "verb 1" 50 + echo "auth-user-pass /etc/openvpn/pia-creds" 51 + echo "<ca>" 52 + cat ${piaSource}/ca.rsa.4096.crt 53 + echo "</ca>" 54 + } > /run/pia-qbittorrent/pia.ovpn 55 + ''; 56 + in 57 + { 58 + # Bootstrap on the host: the container never needs clear-net DNS or HTTPS. 59 + systemd.services."container@pia-qbittorrent" = { 60 + wants = [ "network-online.target" ]; 61 + after = [ "network-online.target" ]; 62 + preStart = lib.mkBefore "${prepare}"; 63 + # NixOS adds the host-side gateway in its postStart, after guest readiness. 64 + postStart = lib.mkAfter "${config.systemd.package}/bin/systemctl -M pia-qbittorrent start pia-openvpn.service"; 65 + serviceConfig.RuntimeDirectory = "pia-qbittorrent"; 66 + serviceConfig.RuntimeDirectoryMode = "0700"; 67 + serviceConfig.TimeoutStartSec = lib.mkForce "2min"; 68 + }; 69 + # NixOS owns this veth; prevent networkd's generic container DHCP/NAT setup. 70 + # networkd matches the full altname too; Unmanaged=true reports Network File: n/a. 71 + # Unlike this match, iptables must never use the overlong altname. 72 + systemd.network.networks."40-pia-qbittorrent" = { 73 + matchConfig.Name = "ve-pia-qbittorrent"; 74 + linkConfig.Unmanaged = true; 75 + }; 76 + containers.pia-qbittorrent = { 77 + autoStart = true; 78 + privateNetwork = true; 79 + enableTun = true; 80 + hostAddress = gateway; 81 + localAddress = "192.168.172.2"; 82 + forwardPorts = [ 83 + { 84 + containerPort = 8080; 85 + hostPort = 8081; 86 + protocol = "tcp"; 87 + } 88 + ]; 89 + bindMounts."/etc/openvpn/pia.ovpn" = { 90 + hostPath = "/run/pia-qbittorrent/pia.ovpn"; 91 + isReadOnly = true; 92 + }; 93 + bindMounts."/etc/openvpn/pia-creds" = { 94 + hostPath = "/run/pia-qbittorrent/pia-creds"; 95 + isReadOnly = true; 96 + }; 97 + # qBittorrent's ONLY writable host path: the constrained staging shelf. 98 + bindMounts."/downloads" = { 99 + hostPath = "/storage/media/.incoming"; 100 + isReadOnly = false; 101 + }; 102 + config = 103 + { 104 + config, 105 + lib, 106 + pkgs, 107 + ... 108 + }: 109 + { 110 + networking.useDHCP = false; 111 + networking.enableIPv6 = false; 112 + networking.nameservers = [ "1.1.1.1" ]; 113 + networking.resolvconf.enable = false; 114 + environment.etc."resolv.conf".text = "nameserver 1.1.1.1\n"; 115 + environment.systemPackages = with pkgs; [ 116 + bash 117 + coreutils 118 + curl 119 + gnugrep 120 + gnused 121 + jq 122 + openvpn 123 + ]; 124 + networking.interfaces.eth0.ipv4.routes = [ 125 + { 126 + address = endpoint; 127 + prefixLength = 32; 128 + via = gateway; 129 + } 130 + ]; 131 + # The bind-mounted staging shelf is Jellyfin-owned on the host (gid 983). 132 + # Match that numeric group inside the container; qBittorrent only sees 133 + # the .incoming bind mount, never the final library. 134 + users.groups.qbittorrent = { }; 135 + users.groups.jellyfin.gid = 983; 136 + users.users.qbittorrent = { 137 + isSystemUser = true; 138 + group = "qbittorrent"; 139 + extraGroups = [ "jellyfin" ]; 140 + home = "/var/lib/qbittorrent"; 141 + createHome = true; 142 + }; 143 + networking.firewall = { 144 + enable = true; 145 + allowedTCPPorts = [ 8080 ]; 146 + # Replace OUTPUT atomically; retain default-deny even on firewall stop. 147 + # Do not allow arbitrary ESTABLISHED flows to fall back onto eth0. 148 + extraCommands = '' 149 + ${pkgs.iptables}/bin/iptables-restore --noflush <<'RULES' 150 + *filter 151 + :OUTPUT DROP [0:0] 152 + :FORWARD DROP [0:0] 153 + -F OUTPUT 154 + -A OUTPUT -o lo -j ACCEPT 155 + -A OUTPUT -o pia -j ACCEPT 156 + -A OUTPUT -o eth0 -d ${endpoint}/32 -p tcp --dport 443 -j ACCEPT 157 + -A OUTPUT -o eth0 -d 100.64.0.0/10 -p tcp --sport 8080 -m conntrack --ctstate ESTABLISHED --ctdir REPLY -j ACCEPT 158 + -A OUTPUT -o eth0 -d ${gateway}/32 -p tcp --sport 8080 -m conntrack --ctstate ESTABLISHED --ctdir REPLY -j ACCEPT 159 + COMMIT 160 + RULES 161 + ${pkgs.iptables}/bin/ip6tables -P OUTPUT DROP 162 + ${pkgs.iptables}/bin/ip6tables -P FORWARD DROP 163 + ''; 164 + }; 165 + systemd.services.qbittorrent = { 166 + wantedBy = [ "pia-openvpn.service" ]; 167 + requires = [ "pia-openvpn.service" ]; 168 + after = [ "pia-openvpn.service" ]; 169 + partOf = [ "pia-openvpn.service" ]; 170 + serviceConfig = { 171 + User = "qbittorrent"; 172 + Group = "qbittorrent"; 173 + StateDirectory = "qbittorrent"; 174 + WorkingDirectory = "/var/lib/qbittorrent"; 175 + ExecStart = "${pkgs.qbittorrent-nox}/bin/qbittorrent-nox --profile=/var/lib/qbittorrent --webui-port=8080"; 176 + Restart = "on-failure"; 177 + RestartSec = "5s"; 178 + }; 179 + }; 180 + systemd.services.pia-openvpn = { 181 + requires = [ "firewall.service" ]; 182 + after = [ 183 + "network-online.target" 184 + "firewall.service" 185 + ]; 186 + wants = [ "network-online.target" ]; 187 + path = [ 188 + pkgs.curl 189 + pkgs.iproute2 190 + pkgs.gnugrep 191 + ]; 192 + serviceConfig = { 193 + Type = "simple"; 194 + Restart = "always"; 195 + RestartSec = "15s"; 196 + ExecStartPre = pkgs.writeShellScript "pia-openvpn-pin" '' 197 + for i in $(seq 1 30); do 198 + if ip -4 route get ${endpoint} 2>/dev/null | grep -q " dev eth0"; then 199 + exit 0 200 + fi 201 + sleep 1 202 + done 203 + exit 1 204 + ''; 205 + ExecStart = "${pkgs.openvpn}/bin/openvpn --config /etc/openvpn/pia.ovpn"; 206 + # Replies to tailnet clients must leave via eth0, not the tunnel: 207 + # the host forwards webui connections from tailnet sources, and the 208 + # host's conntrack only reverses flows that come back through it. 209 + # A dedicated table escapes redirect-gateway's 0.0.0.0/1 in main. 210 + ExecStartPost = pkgs.writeShellScript "pia-openvpn-tailnet-route" '' 211 + ip rule add to 100.64.0.0/10 lookup 100 priority 100 2>/dev/null || true 212 + ip route replace default via ${gateway} dev eth0 table 100 213 + ''; 214 + ExecStopPost = pkgs.writeShellScript "pia-openvpn-tailnet-undo" '' 215 + ip rule del to 100.64.0.0/10 lookup 100 priority 100 2>/dev/null || true 216 + ''; 217 + }; 218 + # Fail closed: the unit only counts as up once the tunnel really 219 + # carries traffic, and qbittorrent requires this unit, so the client 220 + # never starts on a dead tunnel. 221 + postStart = '' 222 + for i in $(seq 1 30); do 223 + if curl --fail --silent --max-time 5 --interface pia \ 224 + https://1.1.1.1/cdn-cgi/trace | grep -q '^ip='; then 225 + exit 0 226 + fi 227 + sleep 1 228 + done 229 + exit 1 230 + ''; 231 + }; 232 + system.stateVersion = "26.05"; 233 + }; 234 + }; 235 + # systemd shortens long veth names; match the private subnet, not that name. 236 + networking.nat = { 237 + enable = true; 238 + externalInterface = "vmbr0"; 239 + internalIPs = [ "192.168.172.0/24" ]; 240 + }; 241 + }
+26
hosts/valefar/secrets.nix
··· 1 + { 2 + age.secrets = { 3 + "pocket-id-encryption-key" = { 4 + file = ../../secrets/pocket-id-encryption-key.age; 5 + mode = "0400"; 6 + }; 7 + "pocket-id-maxmind-license-key" = { 8 + file = ../../secrets/pocket-id-maxmind-license-key.age; 9 + mode = "0400"; 10 + }; 11 + "pia-wireguard-auth.env" = { 12 + file = ../../secrets/pia-wireguard-auth.env.age; 13 + mode = "0400"; 14 + }; 15 + "searx.env" = { 16 + file = ../../secrets/searx.env.age; 17 + mode = "0400"; 18 + }; 19 + "vaultwarden-oidc.env" = { 20 + file = ../../secrets/vaultwarden-oidc.env.age; 21 + owner = "vaultwarden"; 22 + group = "vaultwarden"; 23 + mode = "0400"; 24 + }; 25 + }; 26 + }
+70
hosts/valefar/tests/nat-guard.nix
··· 1 + # Run with the flake's pinned nixpkgs: 2 + # nix build --impure --expr 'let f = builtins.getFlake (toString ./.); in import ./hosts/valefar/tests/nat-guard.nix { pkgs = f.inputs.nixpkgs.legacyPackages.x86_64-linux; }' 3 + { pkgs }: 4 + pkgs.testers.runNixOSTest { 5 + name = "valefar-nat-guard"; 6 + nodes.machine = { lib, ... }: { 7 + imports = [ ../nat-guard.nix ]; 8 + networking.firewall.enable = false; 9 + networking.nat = { 10 + enable = true; 11 + externalInterface = "eth0"; 12 + internalIPs = [ 13 + "192.168.172.0/24" 14 + "192.168.173.0/24" 15 + ]; 16 + }; 17 + systemd.services.nat = { 18 + preStart = "test ! -e /run/fail-nat"; 19 + serviceConfig.RestartSec = lib.mkForce "1s"; 20 + }; 21 + systemd.timers.nat-healthcheck.timerConfig = { 22 + OnBootSec = lib.mkForce "1s"; 23 + OnUnitInactiveSec = lib.mkForce "2s"; 24 + }; 25 + }; 26 + testScript = '' 27 + start_all() 28 + rules = " && ".join([ 29 + "systemctl is-active --quiet nat.service", 30 + "iptables -t nat -C POSTROUTING -j nixos-nat-post", 31 + "iptables -t filter -C FORWARD -j nixos-filter-forward", 32 + *[ 33 + f"iptables -t nat -C nixos-nat-post -s {subnet} -o eth0 -j MASQUERADE" 34 + for subnet in ["192.168.172.0/24", "192.168.173.0/24"] 35 + ], 36 + ]) 37 + 38 + with subtest("boot installs both subnets"): 39 + machine.wait_for_unit("nat.service") 40 + machine.wait_for_unit("nat-healthcheck.timer") 41 + machine.succeed(rules) 42 + 43 + with subtest("healthy checks do not restart NAT"): 44 + invocation = machine.succeed("systemctl show nat -p InvocationID --value") 45 + machine.succeed("systemctl start nat-healthcheck") 46 + assert machine.succeed("systemctl show nat -p InvocationID --value") == invocation 47 + 48 + with subtest("timer recovers a successfully stopped unit"): 49 + machine.succeed("systemctl stop nat") 50 + machine.wait_until_succeeds(rules) 51 + 52 + with subtest("timer repairs a missing subnet in an active unit"): 53 + machine.succeed("iptables -t nat -D nixos-nat-post -s 192.168.173.0/24 -o eth0 -j MASQUERADE") 54 + machine.wait_until_succeeds(rules) 55 + 56 + with subtest("timer repairs a detached chain"): 57 + machine.succeed("iptables -t nat -D POSTROUTING -j nixos-nat-post") 58 + machine.wait_until_succeeds(rules) 59 + 60 + with subtest("failed repair is visible and retried"): 61 + machine.succeed("systemctl stop nat-healthcheck.timer; touch /run/fail-nat") 62 + machine.fail("systemctl restart nat") 63 + machine.fail("systemctl start nat-healthcheck") 64 + machine.succeed("systemctl is-failed nat-healthcheck") 65 + machine.succeed("mv /run/fail-nat /run/nat-failure-tested") 66 + machine.wait_until_succeeds(rules) 67 + machine.succeed("systemctl start nat-healthcheck.timer") 68 + machine.wait_until_succeeds("test $(systemctl show nat-healthcheck -p Result --value) = success") 69 + ''; 70 + }
+63
hosts/valefar/wg-mesh.nix
··· 1 + # wisp.place mesh: full-mesh WireGuard between the wisp servers, 10.88.0.0/24. 2 + # valefar sits behind home NAT, so it dials every peer and keeps the paths open. 3 + # Names resolve through *.mesh.wisp.place; the peer list lives in ~/fleet/mesh.md. 4 + { pkgs, ... }: 5 + { 6 + environment.systemPackages = [ pkgs.wireguard-tools ]; 7 + 8 + # Containers publish ports on 10.88.0.10; start docker once wg0 exists. 9 + systemd.services.docker = { 10 + after = [ "wg-quick-wg0.service" ]; 11 + wants = [ "wg-quick-wg0.service" ]; 12 + }; 13 + 14 + networking.wg-quick.interfaces.wg0 = { 15 + address = [ "10.88.0.10/24" ]; 16 + listenPort = 51820; 17 + privateKeyFile = "/etc/wireguard/wg0.key"; 18 + peers = [ 19 + { 20 + # baal 21 + publicKey = "ooHJ1tE5WVfrC4bAX/japIwNahg71tTSNy94k9d5A0Q="; 22 + allowedIPs = [ "10.88.0.1/32" ]; 23 + endpoint = "150.136.127.67:51820"; 24 + persistentKeepalive = 25; 25 + } 26 + { 27 + # stolas 28 + publicKey = "BYA0fbXxWvgK4uYoYiNGLscki4lTGAsKn98AKlw32B8="; 29 + allowedIPs = [ "10.88.0.2/32" ]; 30 + endpoint = "152.53.121.97:51820"; 31 + persistentKeepalive = 25; 32 + } 33 + { 34 + # sjo1 35 + publicKey = "myd1WecGLdP/DtU198anvcPtEk8Iusa1CASru5kall0="; 36 + allowedIPs = [ "10.88.0.3/32" ]; 37 + endpoint = "152.44.44.138:51820"; 38 + persistentKeepalive = 25; 39 + } 40 + { 41 + # sin1 42 + publicKey = "9mAzm703TerlCQoZP61dyJNnMATGVgVuNtdi+JmKWAo="; 43 + allowedIPs = [ "10.88.0.4/32" ]; 44 + endpoint = "213.163.207.16:51820"; 45 + persistentKeepalive = 25; 46 + } 47 + { 48 + # sharkgirl 49 + publicKey = "3rgSbuy6oz8ePF55yF8ZBS03MDhj3aOq83/PgO+DYng="; 50 + allowedIPs = [ "10.88.0.5/32" ]; 51 + endpoint = "15.204.225.63:51820"; 52 + persistentKeepalive = 25; 53 + } 54 + { 55 + # vine (status page) 56 + publicKey = "Iucesg9GHk9AkjewUIdgOiIBMdLBVzfvrTqLQiNecRI="; 57 + allowedIPs = [ "10.88.0.6/32" ]; 58 + endpoint = "144.225.80.116:51820"; 59 + persistentKeepalive = 25; 60 + } 61 + ]; 62 + }; 63 + }
+22
secrets/pia-wireguard-auth.env.age
··· 1 + age-encryption.org/v1 2 + -> ssh-ed25519 i9wBeA oUdGLb/WpJmjxW7QHnKrcoXdEaX3vlAIh3PMuEMTFXU 3 + xM29aQByEm1QlZe0RAbNfs4w8nVFVnFnoEADqKJLZMQ 4 + -> ssh-ed25519 rgtFBg Dh9lxSdhL5/GwesWVz6iic1oB9hSQaY9a1lJ+MzbXRI 5 + DGVzyVeCHLz35iVwQtIQ7WWNbkOSvrtXh43JM0B62x8 6 + -> ssh-rsa J32+GA 7 + TMQkZcTbx4u9aOfjrGxMRqI+yZdGJlgG1M1Zl+cOAZQxfGlZcFMRmpK+XfiD72No 8 + k5lGctvMFe/yCjrOwoNN/NiE/JmGVjjXh93V3IoSI743C8KAmoHsgt7XgF0bwwuq 9 + +HU2uWp6KjoraZQ8XaQCiD9w87HQqptMFhMUVRXxxDPVVGF8sO9SdN5EX5hpOb9t 10 + eiA8MtyYOnSkwXhsep6Uwac+/OPoc7TOaMWvtIkT1NsMECB4xZ3B8FcFDjwO+jtT 11 + 1eUe0lazmGNkyUf9dSGApfA/c02IUKVqb9drlvYHw0Uj03WDHPLqnGB32i++/Ks+ 12 + Ui/c93wJG743YpeqPadSeZ0UHWFoEPKzgG7NU1rxHq+mvpgMRx/Guc2pkBf9MSrb 13 + 9xL29TMIOxsWiJLjxsun2u9r9AfZoHU/fUJ8Lt1RAOIwV28Jh8tsmF5e+ZaN9NKM 14 + CkIva/aJOjrGtAknbzPTeX6PY9O7D5YQkzEwUD90Qj4d0CmlLxfbTn6RdsVxsPXR 15 + gDV77GqCOYKkScsE+QiRwcjPzWR4kv6Su2IOV77/cHewftgfdCYzMytiDSTI4uDq 16 + 1SQRB2HhjHiT9K1otbCJT2dQNPsLxgkZsKWEgmMoCVGl82IOKOT3EEDz6AFiULRD 17 + bgcMF+S94WbtS8Q/PBdPjIpC54+HzPswlusoOSr1GWI 18 + -> ssh-ed25519 du7llw qFql+PH0TSCLdumpFTVsTNUOsKQbJO6K7nUUXC4Vrzo 19 + NvA2H0/6MQ63J2gPqG0mf8txq+/UNNstXdTHyXlrC/U 20 + --- okqLzInxo2gNlLt6QaamSRGmeYwmMhu0bSTmVYfOPSs 21 + W�͎%��� 22 + �}�Jw��i�,bl ����-����4s�k������RˌG�&&q��j�t����a��P�
secrets/pocket-id-encryption-key.age

This is a binary file and will not be displayed.

secrets/pocket-id-maxmind-license-key.age

This is a binary file and will not be displayed.

+29
secrets/regent.nix
··· 1 + let 2 + regent = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ0pU82lV9dSjkgYbdh9utZ5CDM2dPN70S5fBqN1m3Pb regent@orobas.local"; 3 + users = [ regent ]; 4 + 5 + valefar = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIPu8CVFsnUxhvABEqv4+EBBOL8tva5HJFoV3hElAlD0"; 6 + buer = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMVhjwDcO8eleSoR8a37ZGGPvkHEgV+c8SYcy07SayPB"; 7 + focalor = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA518oTmTp5VG60/dBrLu7rlV1hh8muhMattoiGfmrei"; 8 + systems = [ 9 + valefar 10 + buer 11 + focalor 12 + ]; 13 + in 14 + { 15 + "pocket-id-encryption-key.age".publicKeys = users ++ systems; 16 + "pocket-id-maxmind-license-key.age".publicKeys = users ++ systems; 17 + 18 + "searx.env.age".publicKeys = [ valefar ]; 19 + 20 + "vaultwarden-oidc.env.age".publicKeys = [ 21 + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIPu8CVFsnUxhvABEqv4+EBBOL8tva5HJFoV3hElAlD0 root@valefar" 22 + ]; 23 + "pia-wireguard-auth.env.age".publicKeys = [ 24 + regent 25 + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ7Y9Je7H3gC72cgdEH4wifUDsmhKMeU5Z4oL1s1WcSE niri@nekomimi.pet" 26 + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCM1KHYX8icUv9XxV0QDgsaXE05nA8gaZ0O3OwP/vH0olpeXc13FxoFKvNVYo1aWCo8xjv9G01JrWNhBRFf76LyOJhL1cm3Psorcx0V7sdbRy9gWnPRR5tA58wKr51yvz/4I+KsrO537cTAVZQky9J2wDh9jDpiNQJN80Kv9RdrFc3BVrEXUrsE3YtsNiSg4YoAPD4vhLupVohMPoh/w3dtdTQBu+YF+1rjMJ9Xf22DaVqz6l95T/Zui1smQnIZbjXRibJ8RR3Kla7K94nqVvNMVURiK71vXh+bJvFE2HyDDNsVteAv2DxpXQJpojxGQUXvR1LGQP9Lm5yhdtTqZMeuTDOKCCTy3yMGX4tX09ZyP5S0WwgTh2vE33C4+gUFa4wSeGZj7IH2t4ivgxK17fkGIeMcPpPuwkIWbinCp5AXPBqly5OBry6Qyg8SN1jb+d6pW3sn3slsikH53B4Qx5PP1ywCSuKnboOn9pQqZt7uvs75W2oyYxdmh/SNx//dcNX16YLYJ1KfaGpxerT6AjfiHSB8Vp8n6N7bVmizsqg0nkU+SmF8gh3UKidyg2RzL+zcJcxp+APiumM0rs3fXFNBuT8VncOPbiyjSw/NUt3EiuBGIap5mnJF+zW965YqZAo+Upivqltdah0E9WJrF4t7Z5+FyuLi3N5ovyROEEAlNw== root@valefar" 27 + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIPu8CVFsnUxhvABEqv4+EBBOL8tva5HJFoV3hElAlD0 root@valefar" 28 + ]; 29 + }
+1 -1
secrets/secrets.nix
··· 40 40 yusdacra 41 41 trimounts 42 42 ]; 43 - } 43 + } // import ./regent.nix
secrets/vaultwarden-oidc.env.age

This is a binary file and will not be displayed.

+42
users/regent/default.nix
··· 1 + { lib, pkgs, ... }: 2 + { 3 + users.mutableUsers = lib.mkForce true; 4 + 5 + users.users.regent = { 6 + isNormalUser = true; 7 + shell = pkgs.zsh; 8 + extraGroups = [ 9 + "docker" 10 + "wheel" 11 + "input" 12 + ]; 13 + packages = with pkgs; [ tree ]; 14 + openssh.authorizedKeys.keys = [ 15 + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGI8jgqru/3LFgk12C9Zc/NL5di5+jGocQZi/dA73ZRr regent@regentsmacbookair.dns.sharkgirl.pet" 16 + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCS9VBRE13jojnqVjuUZWTcOK8GokDDlk2U0i61vEJizVzNowGnIAbwq0cOaFEBX4JBkOa4I8Ku2Pw7fODuoehSK/t7FrfXExk2PBT3k0mfzqQYxfq5bzae7AWr7n/sKUBTtvHSACfidxzQpV7VSgW68jqdOt6h7FHSeS2jac7wUNPobL0uCkFB4FiEQOnIqlRGSSabVemL7bC9H9lUyOODSTthiq9S3pPYknyHDRKUtSCSw4pfpasr4bxDVSW99h3GBcW0hZbpw5bwlxQlwbclxQDnn7XJhWpq6zL/2ScVGJgd94z7FshKoF5IFTk6e7a/Ouv4Ato4hRLxEe5u70CH ssh-key-2023-07-11" 17 + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ7Y9Je7H3gC72cgdEH4wifUDsmhKMeU5Z4oL1s1WcSE niri@nekomimi.pet" 18 + ]; 19 + }; 20 + 21 + users.users.root = { 22 + openssh.authorizedKeys.keys = [ 23 + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCS9VBRE13jojnqVjuUZWTcOK8GokDDlk2U0i61vEJizVzNowGnIAbwq0cOaFEBX4JBkOa4I8Ku2Pw7fODuoehSK/t7FrfXExk2PBT3k0mfzqQYxfq5bzae7AWr7n/sKUBTtvHSACfidxzQpV7VSgW68jqdOt6h7FHSeS2jac7wUNPobL0uCkFB4FiEQOnIqlRGSSabVemL7bC9H9lUyOODSTthiq9S3pPYknyHDRKUtSCSw4pfpasr4bxDVSW99h3GBcW0hZbpw5bwlxQlwbclxQDnn7XJhWpq6zL/2ScVGJgd94z7FshKoF5IFTk6e7a/Ouv4Ato4hRLxEe5u70CH ssh-key-2023-07-11" 24 + ]; 25 + }; 26 + 27 + programs.git = { 28 + enable = true; 29 + config = { 30 + user.name = "waveringana"; 31 + user.email = "ana@nekomimi.pet"; 32 + init = { 33 + defaultBranch = "main"; 34 + }; 35 + }; 36 + }; 37 + 38 + security.sudo.enable = true; 39 + security.sudo.wheelNeedsPassword = false; 40 + 41 + programs.zsh.enable = true; 42 + }
+324
users/regent/home.nix
··· 1 + { lib, pkgs, ... }: 2 + { 3 + 4 + programs.noctalia = { 5 + enable = true; 6 + settings = { 7 + theme = { 8 + mode = "dark"; 9 + source = "builtin"; 10 + builtin = "Catppuccin"; 11 + }; 12 + }; 13 + }; 14 + 15 + home.username = "regent"; 16 + home.homeDirectory = "/home/regent"; 17 + home.stateVersion = "25.05"; 18 + home.sessionVariables = { 19 + EDITOR = lib.mkForce "nvim"; 20 + VISUAL = lib.mkForce "nvim"; 21 + }; 22 + 23 + catppuccin = { 24 + enable = true; 25 + autoEnable = true; 26 + flavor = "mocha"; 27 + accent = "rosewater"; 28 + ghostty.enable = true; 29 + nvim.enable = true; 30 + }; 31 + 32 + programs.ghostty = { 33 + enable = true; 34 + settings = { 35 + font-size = 18; 36 + font-family = "Comic Mono"; 37 + theme = "catppuccin-mocha"; 38 + background = "#1e1e1e"; 39 + background-opacity = 0.8; 40 + background-blur = 8; 41 + }; 42 + }; 43 + 44 + programs.zsh = { 45 + enable = true; 46 + enableCompletion = true; 47 + autosuggestion.enable = true; 48 + syntaxHighlighting.enable = true; 49 + shellAliases = { 50 + zed = "zeditor"; 51 + }; 52 + oh-my-zsh = { 53 + enable = true; 54 + plugins = [ "git" ]; 55 + theme = "robbyrussell"; 56 + }; 57 + }; 58 + 59 + programs.neovim = { 60 + enable = true; 61 + defaultEditor = true; 62 + viAlias = true; 63 + vimAlias = true; 64 + withPython3 = false; 65 + withRuby = false; 66 + extraPackages = with pkgs; [ 67 + bash-language-server 68 + lua-language-server 69 + nil 70 + nixfmt 71 + pyright 72 + ripgrep 73 + rust-analyzer 74 + stylua 75 + typescript-language-server 76 + ]; 77 + plugins = with pkgs.vimPlugins; [ 78 + cmp-nvim-lsp 79 + gitsigns-nvim 80 + lualine-nvim 81 + luasnip 82 + nvim-cmp 83 + nvim-lspconfig 84 + nvim-treesitter.withAllGrammars 85 + plenary-nvim 86 + telescope-fzf-native-nvim 87 + telescope-nvim 88 + which-key-nvim 89 + ]; 90 + initLua = '' 91 + vim.g.mapleader = " " 92 + vim.g.maplocalleader = " " 93 + vim.opt.number = true 94 + vim.opt.relativenumber = true 95 + vim.opt.mouse = "a" 96 + vim.opt.clipboard = "unnamedplus" 97 + vim.opt.breakindent = true 98 + vim.opt.undofile = true 99 + vim.opt.ignorecase = true 100 + vim.opt.smartcase = true 101 + vim.opt.signcolumn = "yes" 102 + vim.opt.updatetime = 250 103 + vim.opt.timeoutlen = 300 104 + vim.opt.splitright = true 105 + vim.opt.splitbelow = true 106 + vim.opt.expandtab = true 107 + vim.opt.shiftwidth = 2 108 + vim.opt.tabstop = 2 109 + 110 + require("lualine").setup({ options = { theme = "catppuccin" } }) 111 + require("gitsigns").setup() 112 + require("which-key").setup() 113 + 114 + local telescope = require("telescope") 115 + telescope.setup({ defaults = { path_display = { "smart" } } }) 116 + pcall(telescope.load_extension, "fzf") 117 + local builtin = require("telescope.builtin") 118 + vim.keymap.set("n", "<leader>ff", builtin.find_files, { desc = "Find files" }) 119 + vim.keymap.set("n", "<leader>fg", builtin.live_grep, { desc = "Live grep" }) 120 + vim.keymap.set("n", "<leader>fb", builtin.buffers, { desc = "Buffers" }) 121 + vim.keymap.set("n", "<leader>fh", builtin.help_tags, { desc = "Help" }) 122 + 123 + local cmp = require("cmp") 124 + local luasnip = require("luasnip") 125 + cmp.setup({ 126 + snippet = { expand = function(args) luasnip.lsp_expand(args.body) end }, 127 + mapping = cmp.mapping.preset.insert({ 128 + ["<C-Space>"] = cmp.mapping.complete(), 129 + ["<CR>"] = cmp.mapping.confirm({ select = true }), 130 + ["<Tab>"] = cmp.mapping.select_next_item(), 131 + ["<S-Tab>"] = cmp.mapping.select_prev_item(), 132 + }), 133 + sources = cmp.config.sources({ { name = "nvim_lsp" } }), 134 + }) 135 + 136 + local capabilities = require("cmp_nvim_lsp").default_capabilities() 137 + for _, server in ipairs({ "bashls", "lua_ls", "nil_ls", "pyright", "rust_analyzer", "ts_ls" }) do 138 + vim.lsp.config(server, { capabilities = capabilities }) 139 + vim.lsp.enable(server) 140 + end 141 + vim.keymap.set("n", "gd", vim.lsp.buf.definition, { desc = "Go to definition" }) 142 + vim.keymap.set("n", "gr", vim.lsp.buf.references, { desc = "References" }) 143 + vim.keymap.set("n", "K", vim.lsp.buf.hover, { desc = "Hover documentation" }) 144 + vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, { desc = "Rename" }) 145 + vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, { desc = "Code action" }) 146 + vim.keymap.set("n", "<leader>f", function() vim.lsp.buf.format({ async = true }) end, { desc = "Format" }) 147 + ''; 148 + }; 149 + 150 + home.pointerCursor = { 151 + enable = true; 152 + gtk.enable = true; 153 + package = pkgs.phinger-cursors; 154 + name = "Phinger-cursors-light"; 155 + size = 32; 156 + }; 157 + 158 + gtk = { 159 + enable = true; 160 + font = { 161 + name = "Comic Neue"; 162 + size = 11; 163 + }; 164 + }; 165 + 166 + xdg.configFile."niri/config.kdl".text = '' 167 + input { 168 + keyboard { 169 + xkb { 170 + layout "us" 171 + } 172 + } 173 + touchpad { 174 + tap 175 + natural-scroll 176 + } 177 + mouse { 178 + accel-speed -0.25 179 + accel-profile "flat" 180 + } 181 + focus-follows-mouse max-scroll-amount="0%" 182 + } 183 + 184 + window-rule { 185 + geometry-corner-radius 20 186 + 187 + clip-to-geometry true 188 + } 189 + 190 + window-rule { 191 + match app-id=r#"(?i)steam_app|aoe2|wine"# 192 + geometry-corner-radius 0 193 + clip-to-geometry false 194 + } 195 + 196 + window-rule { 197 + match app-id="dev.noctalia.Noctalia" 198 + open-floating true 199 + default-column-width { fixed 1080; } 200 + default-window-height { fixed 920; } 201 + } 202 + 203 + debug { 204 + render-drm-device "/dev/dri/renderD129" 205 + ignore-drm-device "/dev/dri/renderD128" 206 + honor-xdg-activation-with-invalid-serial 207 + } 208 + 209 + layer-rule { 210 + match namespace="^noctalia-backdrop" 211 + place-within-backdrop true 212 + } 213 + 214 + layer-rule { 215 + match namespace="^noctalia-wallpaper" 216 + place-within-backdrop true 217 + } 218 + 219 + overview { 220 + workspace-shadow { 221 + off 222 + } 223 + } 224 + 225 + window-rule { 226 + exclude app-id="com\\.mitchellh\\.ghostty" 227 + background-effect { 228 + blur true 229 + xray false 230 + } 231 + } 232 + 233 + layer-rule { 234 + match namespace="^noctalia-(bar-[^\"]+|notification|dock|panel|attached-panel|osd)$" 235 + background-effect { 236 + xray false 237 + } 238 + } 239 + 240 + blur { 241 + passes 2 242 + offset 3.0 243 + noise 0.03 244 + saturation 1.0 245 + } 246 + 247 + layout { 248 + background-color "transparent" 249 + gaps 12 250 + center-focused-column "never" 251 + default-column-width { 252 + proportion 0.5 253 + } 254 + focus-ring { 255 + width 2 256 + active-color "#89b4fa" 257 + inactive-color "#45475a" 258 + } 259 + border { 260 + off 261 + } 262 + } 263 + 264 + prefer-no-csd 265 + screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png" 266 + spawn-at-startup "noctalia" 267 + spawn-at-startup "xwayland-satellite" 268 + 269 + // Match by make/model/serial: connector names shift across GPU/driver 270 + // updates (HDMI-A-3 to HDMI-A-1, DP-4 to DP-2) and silently orphan the blocks. 271 + output "Microstep MAG 341C OLED 0x01010101" { 272 + mode "3440x1440@174.962" 273 + position x=0 y=0 274 + } 275 + 276 + output "ASUSTek COMPUTER INC ASUS PA279CV R9LMTF061509" { 277 + mode "3840x2160@59.997" 278 + scale 1.5 279 + position x=3440 y=0 280 + } 281 + 282 + binds { 283 + Mod+Return { spawn "ghostty"; } 284 + Mod+Space { spawn-sh "noctalia msg panel-toggle launcher"; } 285 + Mod+D { spawn-sh "noctalia msg panel-toggle launcher"; } 286 + Mod+S { spawn-sh "noctalia msg panel-toggle control-center"; } 287 + Mod+Comma { spawn-sh "noctalia msg settings-toggle"; } 288 + Mod+Alt+L { spawn "noctalia" "ipc" "call" "lockScreen" "lock"; } 289 + Mod+Shift+Escape { quit; } 290 + Mod+Q { close-window; } 291 + Mod+H { focus-column-left; } 292 + Mod+J { focus-window-down; } 293 + Mod+K { focus-window-up; } 294 + Mod+L { focus-column-right; } 295 + Mod+Shift+Q { move-column-left; } 296 + Mod+Shift+J { move-window-down; } 297 + Mod+Shift+K { move-window-up; } 298 + Mod+Shift+E { move-column-right; } 299 + Mod+Left { focus-column-left; } 300 + Mod+Down { focus-window-down; } 301 + Mod+Up { focus-window-up; } 302 + Mod+Right { focus-column-right; } 303 + Mod+Shift+Left { move-column-left; } 304 + Mod+Shift+Down { move-window-down; } 305 + Mod+Shift+Up { move-window-up; } 306 + Mod+Shift+Right { move-column-right; } 307 + Mod+1 { focus-workspace 1; } 308 + Mod+2 { focus-workspace 2; } 309 + Mod+3 { focus-workspace 3; } 310 + Mod+4 { focus-workspace 4; } 311 + Mod+5 { focus-workspace 5; } 312 + Mod+Shift+X { maximize-column; } 313 + Mod+Shift+F { fullscreen-window; } 314 + Mod+C { center-column; } 315 + Mod+V { toggle-window-floating; } 316 + Print { screenshot; } 317 + XF86AudioRaiseVolume allow-when-locked=true { spawn-sh "noctalia msg volume-up"; } 318 + XF86AudioLowerVolume allow-when-locked=true { spawn-sh "noctalia msg volume-down"; } 319 + XF86AudioMute allow-when-locked=true { spawn-sh "noctalia msg volume-mute"; } 320 + XF86MonBrightnessUp { spawn-sh "noctalia msg brightness-up"; } 321 + XF86MonBrightnessDown { spawn-sh "noctalia msg brightness-down"; } 322 + } 323 + ''; 324 + }
+9
secrets/searx.env.age
··· 1 + -----BEGIN AGE ENCRYPTED FILE----- 2 + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGR1N2xsdyBwTG81 3 + WlAremsybnJJKytTL1VXOWhRNlNQQ0IxTWtxL0lnQVI0VUlXOW0wClQvRll2Q1lL 4 + RkRCckp2ZTFpTWxIVUlPV0s0NWhwZU9nRXJoRUJUN1VySHMKLS0tIG1YZExFemJy 5 + cFBOWjAvcDVIemRsazlOaDBkWGZXSE5EZm54ZE5LVnR6RUUKJHUeCHZPsNCsOrix 6 + 9ESiqGnKgZs3vxGXlEeNjvXT5uAkOkP1AVhAItk8lUsK9uwttS1aB1/IHBNP2Tpl 7 + F95BpbMYqo5bIqMDhCdP3a3EhOIR4Xd+ciqS4gRXR5vwHZBGZGtFQjztiONXmM5I 8 + qADJN5nY 9 + -----END AGE ENCRYPTED FILE-----

History

2 rounds 0 comments
Sign up or Login to add to the discussion
2 commits
Expand
Port focalor and valefar into ark
Preserve Valefar search and printing services
Expand 0 comments
Pull request successfully merged
okami.mom submitted #0
1 commit
Expand
Port focalor and valefar into ark
Expand 0 comments