yep, more dotfiles

Compare changes

Choose any two refs to compare.

Changed files
+1631 -2326
apps
home-manager
hosts
lib
modules
nixos
pkgs
secrets
templates
blank
c
rust
rust-pkg
+1
.ignore
··· 1 + *.age
+1 -1
Justfile
··· 25 25 nixos-rebuild switch \ 26 26 --flake .#{{host}} \ 27 27 --target-host {{host}} \ 28 - --use-remote-sudo {{args}} 28 + --sudo {{args}}
+3 -3
README.md
··· 20 20 - `fragments`: Home Manager configuration fragments 21 21 - `options`: Home Manager configuration flags 22 22 - `profiles`: Base Home Manager configurations to build upon (e.g. `desktop`, `minimal`) 23 - - `lib`: Additional custom lib and flake helpers 23 + - `lib`: Additional custom lib and flake helpers 24 24 - `modules`: modules that fill a missing feature of NixOS or Home Manager 25 25 - `nixos`: NixOS related config 26 26 - `hardware/<hostname>.nix`: Device-specific settings like settings generated by `nixos-generate-config` ··· 83 83 84 84 ## Make a backup 85 85 86 - Pull up your favorite ArchaicBakup disc 86 + Pull up your favorite ArchaicBackup disc 87 87 88 - - Set environnement variables 88 + - Set environment variables 89 89 90 90 `RESTIC_REPOSITORY`: `/run/media/user/discname/` 91 91 `RESTIC_PASSWORD_FILE`: ?
+1 -1
apps/flash-installer.nix
··· 29 29 fi 30 30 31 31 echo "Flashing to $dev" 32 - 32 + 33 33 # Format selected disk 34 34 pv -tpreb "${isoPath}" | sudo dd bs=4M of="$dev" iflag=fullblock conv=notrunc,noerror oflag=sync 35 35 '';
+2 -1
configurations.nix
··· 19 19 20 20 # Servers 21 21 "weird-row-server" = createSystem pkgs [ 22 - (system "weird-row-server" "server") 22 + (host "weird-row-server") 23 23 (managedDiskLayout "ext4-hetzner" { device = "sda"; swapSize = 2; }) 24 + # TODO: should we keep a real user there? 24 25 (user "milomoisson" { description = "Milo Moisson"; profile = "server"; keys = keys.users; }) 25 26 ]; 26 27 };
+189 -164
flake.lock
··· 24 24 ], 25 25 "nixpkgs": [ 26 26 "nixpkgs" 27 - ] 27 + ], 28 + "systems": "systems" 28 29 }, 29 30 "locked": { 30 - "lastModified": 1703089996, 31 - "narHash": "sha256-ipqShkBmHKC9ft1ZAsA6aeKps32k7+XZSPwfxeHLsAU=", 31 + "lastModified": 1762618334, 32 + "narHash": "sha256-wyT7Pl6tMFbFrs8Lk/TlEs81N6L+VSybPfiIgzU8lbQ=", 32 33 "owner": "ryantm", 33 34 "repo": "agenix", 34 - "rev": "564595d0ad4be7277e07fa63b5a991b3c645655d", 35 + "rev": "fcdea223397448d35d9b31f798479227e80183f6", 35 36 "type": "github" 36 37 }, 37 38 "original": { 38 39 "owner": "ryantm", 39 - "ref": "0.15.0", 40 40 "repo": "agenix", 41 41 "type": "github" 42 42 } ··· 46 46 "fromYaml": "fromYaml" 47 47 }, 48 48 "locked": { 49 - "lastModified": 1746562888, 50 - "narHash": "sha256-YgNJQyB5dQiwavdDFBMNKk1wyS77AtdgDk/VtU6wEaI=", 49 + "lastModified": 1755819240, 50 + "narHash": "sha256-qcMhnL7aGAuFuutH4rq9fvAhCpJWVHLcHVZLtPctPlo=", 51 51 "owner": "SenchoPens", 52 52 "repo": "base16.nix", 53 - "rev": "806a1777a5db2a1ef9d5d6f493ef2381047f2b89", 53 + "rev": "75ed5e5e3fce37df22e49125181fa37899c3ccd6", 54 54 "type": "github" 55 55 }, 56 56 "original": { ··· 79 79 "base16-helix": { 80 80 "flake": false, 81 81 "locked": { 82 - "lastModified": 1748408240, 83 - "narHash": "sha256-9M2b1rMyMzJK0eusea0x3lyh3mu5nMeEDSc4RZkGm+g=", 82 + "lastModified": 1752979451, 83 + "narHash": "sha256-0CQM+FkYy0fOO/sMGhOoNL80ftsAzYCg9VhIrodqusM=", 84 84 "owner": "tinted-theming", 85 85 "repo": "base16-helix", 86 - "rev": "6c711ab1a9db6f51e2f6887cc3345530b33e152e", 86 + "rev": "27cf1e66e50abc622fb76a3019012dc07c678fac", 87 87 "type": "github" 88 88 }, 89 89 "original": { ··· 111 111 }, 112 112 "crane": { 113 113 "locked": { 114 - "lastModified": 1754269165, 115 - "narHash": "sha256-0tcS8FHd4QjbCVoxN9jI+PjHgA4vc/IjkUSp+N3zy0U=", 114 + "lastModified": 1765145449, 115 + "narHash": "sha256-aBVHGWWRzSpfL++LubA0CwOOQ64WNLegrYHwsVuVN7A=", 116 116 "owner": "ipetkov", 117 117 "repo": "crane", 118 - "rev": "444e81206df3f7d92780680e45858e31d2f07a08", 118 + "rev": "69f538cdce5955fcd47abfed4395dc6d5194c1c5", 119 119 "type": "github" 120 120 }, 121 121 "original": { ··· 132 132 ] 133 133 }, 134 134 "locked": { 135 - "lastModified": 1673295039, 136 - "narHash": "sha256-AsdYgE8/GPwcelGgrntlijMg4t3hLFJFCRF3tL5WVjA=", 135 + "lastModified": 1744478979, 136 + "narHash": "sha256-dyN+teG9G82G+m+PX/aSAagkC+vUv0SgUw3XkPhQodQ=", 137 137 "owner": "lnl7", 138 138 "repo": "nix-darwin", 139 - "rev": "87b9d090ad39b25b2400029c64825fc2a8868943", 139 + "rev": "43975d782b418ebf4969e9ccba82466728c2851b", 140 140 "type": "github" 141 141 }, 142 142 "original": { ··· 153 153 ] 154 154 }, 155 155 "locked": { 156 - "lastModified": 1736864502, 157 - "narHash": "sha256-ItkIZyebGvNH2dK9jVGzJHGPtb6BSWLN8Gmef16NeY0=", 156 + "lastModified": 1764627417, 157 + "narHash": "sha256-D6xc3Rl8Ab6wucJWdvjNsGYGSxNjQHzRc2EZ6eeQ6l4=", 158 158 "owner": "nix-community", 159 159 "repo": "disko", 160 - "rev": "0141aabed359f063de7413f80d906e1d98c0c123", 160 + "rev": "5a88a6eceb8fd732b983e72b732f6f4b8269bef3", 161 161 "type": "github" 162 162 }, 163 163 "original": { 164 164 "owner": "nix-community", 165 - "ref": "v1.11.0", 166 165 "repo": "disko", 167 166 "type": "github" 168 167 } ··· 170 169 "firefox-gnome-theme": { 171 170 "flake": false, 172 171 "locked": { 173 - "lastModified": 1748383148, 174 - "narHash": "sha256-pGvD/RGuuPf/4oogsfeRaeMm6ipUIznI2QSILKjKzeA=", 172 + "lastModified": 1758112371, 173 + "narHash": "sha256-lizRM2pj6PHrR25yimjyFn04OS4wcdbc38DCdBVa2rk=", 175 174 "owner": "rafaelmardojai", 176 175 "repo": "firefox-gnome-theme", 177 - "rev": "4eb2714fbed2b80e234312611a947d6cb7d70caf", 176 + "rev": "0909cfe4a2af8d358ad13b20246a350e14c2473d", 178 177 "type": "github" 179 178 }, 180 179 "original": { ··· 186 185 "flake-compat": { 187 186 "flake": false, 188 187 "locked": { 189 - "lastModified": 1747046372, 190 - "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", 188 + "lastModified": 1761588595, 189 + "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=", 191 190 "owner": "edolstra", 192 191 "repo": "flake-compat", 193 - "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", 192 + "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", 194 193 "type": "github" 195 194 }, 196 195 "original": { ··· 200 199 } 201 200 }, 202 201 "flake-compat_2": { 202 + "locked": { 203 + "lastModified": 1761588595, 204 + "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=", 205 + "owner": "edolstra", 206 + "repo": "flake-compat", 207 + "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", 208 + "type": "github" 209 + }, 210 + "original": { 211 + "owner": "edolstra", 212 + "repo": "flake-compat", 213 + "type": "github" 214 + } 215 + }, 216 + "flake-compat_3": { 203 217 "flake": false, 204 218 "locked": { 205 219 "lastModified": 1751685974, ··· 214 228 } 215 229 }, 216 230 "flake-parts": { 217 - "inputs": { 218 - "nixpkgs-lib": [ 219 - "lanzaboote", 220 - "nixpkgs" 221 - ] 222 - }, 223 - "locked": { 224 - "lastModified": 1754091436, 225 - "narHash": "sha256-XKqDMN1/Qj1DKivQvscI4vmHfDfvYR2pfuFOJiCeewM=", 226 - "owner": "hercules-ci", 227 - "repo": "flake-parts", 228 - "rev": "67df8c627c2c39c41dbec76a1f201929929ab0bd", 229 - "type": "github" 230 - }, 231 - "original": { 232 - "owner": "hercules-ci", 233 - "repo": "flake-parts", 234 - "type": "github" 235 - } 236 - }, 237 - "flake-parts_2": { 238 231 "inputs": { 239 232 "nixpkgs-lib": [ 240 233 "stylix", ··· 242 235 ] 243 236 }, 244 237 "locked": { 245 - "lastModified": 1749398372, 246 - "narHash": "sha256-tYBdgS56eXYaWVW3fsnPQ/nFlgWi/Z2Ymhyu21zVM98=", 238 + "lastModified": 1756770412, 239 + "narHash": "sha256-+uWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw=", 247 240 "owner": "hercules-ci", 248 241 "repo": "flake-parts", 249 - "rev": "9305fe4e5c2a6fcf5ba6a3ff155720fbe4076569", 242 + "rev": "4524271976b625a4a605beefd893f270620fd751", 250 243 "type": "github" 251 244 }, 252 245 "original": { ··· 257 250 }, 258 251 "flake-utils": { 259 252 "inputs": { 260 - "systems": "systems_2" 253 + "systems": "systems_3" 261 254 }, 262 255 "locked": { 263 256 "lastModified": 1694529238, ··· 298 291 "rust-overlay": "rust-overlay" 299 292 }, 300 293 "locked": { 301 - "lastModified": 1747139554, 302 - "narHash": "sha256-CpjdfdzyN0tAcBvtg9AQk+mDlNSb+NAZPUBpx/4VzvA=", 294 + "lastModified": 1764804992, 295 + "narHash": "sha256-6XIwDQwquGUfiwoTsukMyE6DcXW4Cx0fjE4cLTgQ7RM=", 303 296 "owner": "mrnossiom", 304 297 "repo": "git-leave", 305 - "rev": "bf125663fa992097620ca034ec57ebd20ed50532", 298 + "rev": "3c09ab6afafae76be08956cc7bf563f80e0f8394", 306 299 "type": "github" 307 300 }, 308 301 "original": { ··· 319 312 ] 320 313 }, 321 314 "locked": { 322 - "lastModified": 1709087332, 323 - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", 315 + "lastModified": 1762808025, 316 + "narHash": "sha256-XmjITeZNMTQXGhhww6ed/Wacy2KzD6svioyCX7pkUu4=", 324 317 "owner": "hercules-ci", 325 318 "repo": "gitignore.nix", 326 - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", 319 + "rev": "cb5e3fdca1de58ccbc3ef53de65bd372b48f567c", 327 320 "type": "github" 328 321 }, 329 322 "original": { ··· 357 350 "inputs": { 358 351 "nixpkgs": [ 359 352 "lanzaboote", 360 - "pre-commit-hooks-nix", 353 + "pre-commit", 361 354 "nixpkgs" 362 355 ] 363 356 }, ··· 399 392 "gnome-shell": { 400 393 "flake": false, 401 394 "locked": { 402 - "lastModified": 1744584021, 403 - "narHash": "sha256-0RJ4mJzf+klKF4Fuoc8VN8dpQQtZnKksFmR2jhWE1Ew=", 395 + "host": "gitlab.gnome.org", 396 + "lastModified": 1762869044, 397 + "narHash": "sha256-nwm/GJ2Syigf7VccLAZ66mFC8mZJFqpJmIxSGKl7+Ds=", 404 398 "owner": "GNOME", 405 399 "repo": "gnome-shell", 406 - "rev": "52c517c8f6c199a1d6f5118fae500ef69ea845ae", 407 - "type": "github" 400 + "rev": "680e3d195a92203f28d4bf8c6e8bb537cc3ed4ad", 401 + "type": "gitlab" 408 402 }, 409 403 "original": { 404 + "host": "gitlab.gnome.org", 410 405 "owner": "GNOME", 411 - "ref": "48.1", 406 + "ref": "gnome-49", 412 407 "repo": "gnome-shell", 413 - "type": "github" 408 + "type": "gitlab" 414 409 } 415 410 }, 416 411 "gomod2nix": { ··· 442 437 ] 443 438 }, 444 439 "locked": { 445 - "lastModified": 1758463745, 446 - "narHash": "sha256-uhzsV0Q0I9j2y/rfweWeGif5AWe0MGrgZ/3TjpDYdGA=", 440 + "lastModified": 1764398914, 441 + "narHash": "sha256-YPrpwlVQidzQlMh0OnquaJR+58rKe9YNnuRis293Ilo=", 447 442 "owner": "nix-community", 448 443 "repo": "home-manager", 449 - "rev": "3b955f5f0a942f9f60cdc9cacb7844335d0f21c3", 444 + "rev": "d0c5fdc48db6f19471b8adc954eca09194e68036", 450 445 "type": "github" 451 446 }, 452 447 "original": { 453 448 "owner": "nix-community", 454 - "ref": "release-25.05", 449 + "ref": "release-25.11", 455 450 "repo": "home-manager", 456 451 "type": "github" 457 452 } ··· 489 484 "rust-overlay": "rust-overlay_2" 490 485 }, 491 486 "locked": { 492 - "lastModified": 1763654528, 493 - "narHash": "sha256-FngaJrwCZT+SSiw4BrkepYoW6SwZtzVg/1nX9k6nUdc=", 487 + "lastModified": 1764455123, 488 + "narHash": "sha256-ePlZ3OHBLTRlRa9zs2IeHNA8CuiMLyu5ZUzxoGI5Hp0=", 494 489 "owner": "pixilie", 495 490 "repo": "hypixel-bank-tracker", 496 - "rev": "047d3bcabae9c7aa87214f92292a8ee3550927cc", 491 + "rev": "8e4e91b1a8fa264895fa40dda93c3363be33a958", 497 492 "type": "github" 498 493 }, 499 494 "original": { ··· 547 542 "lanzaboote": { 548 543 "inputs": { 549 544 "crane": "crane", 550 - "flake-compat": "flake-compat", 551 - "flake-parts": "flake-parts", 552 545 "nixpkgs": [ 553 546 "unixpkgs" 554 547 ], 555 - "pre-commit-hooks-nix": "pre-commit-hooks-nix", 548 + "pre-commit": "pre-commit", 556 549 "rust-overlay": "rust-overlay_3" 557 550 }, 558 551 "locked": { 559 - "lastModified": 1762205063, 560 - "narHash": "sha256-If6vQ+KvtKs3ARBO9G3l+4wFSCYtRBrwX1z+I+B61wQ=", 552 + "lastModified": 1765382359, 553 + "narHash": "sha256-RJmgVDzjRI18BWVogG6wpsl1UCuV6ui8qr4DJ1LfWZ8=", 561 554 "owner": "nix-community", 562 555 "repo": "lanzaboote", 563 - "rev": "88b8a563ff5704f4e8d8e5118fb911fa2110ca05", 556 + "rev": "e8c096ade12ec9130ff931b0f0e25d2f1bc63607", 564 557 "type": "github" 565 558 }, 566 559 "original": { 567 560 "owner": "nix-community", 568 - "ref": "v0.4.3", 569 561 "repo": "lanzaboote", 570 562 "type": "github" 571 563 } ··· 583 575 "url": "https://github.com/lucide-icons/lucide/releases/download/0.536.0/lucide-icons-0.536.0.zip" 584 576 } 585 577 }, 578 + "nix-alien": { 579 + "inputs": { 580 + "flake-compat": "flake-compat_2", 581 + "nix-index-database": "nix-index-database", 582 + "nixpkgs": [ 583 + "nixpkgs" 584 + ] 585 + }, 586 + "locked": { 587 + "lastModified": 1764061716, 588 + "narHash": "sha256-xKnIoMPv2kIsWhjRhJayqMWU2xkjeq2pyPmR1dLFPHs=", 589 + "owner": "thiagokokada", 590 + "repo": "nix-alien", 591 + "rev": "9bc9c1ab671eb1b610f549e15bc0b750ab987409", 592 + "type": "github" 593 + }, 594 + "original": { 595 + "owner": "thiagokokada", 596 + "repo": "nix-alien", 597 + "type": "github" 598 + } 599 + }, 586 600 "nix-darwin": { 587 601 "inputs": { 588 602 "nixpkgs": [ ··· 590 604 ] 591 605 }, 592 606 "locked": { 593 - "lastModified": 1762912391, 594 - "narHash": "sha256-4hpBE7bGd24SfD28rzMdUGXsLsNEYxCCrTipFdoqoNM=", 595 - "owner": "LnL7", 607 + "lastModified": 1764161084, 608 + "narHash": "sha256-HN84sByg9FhJnojkGGDSrcjcbeioFWoNXfuyYfJ1kBE=", 609 + "owner": "nix-darwin", 596 610 "repo": "nix-darwin", 597 - "rev": "d76299b2cd01837c4c271a7b5186e3d5d8ebd126", 611 + "rev": "e95de00a471d07435e0527ff4db092c84998698e", 598 612 "type": "github" 599 613 }, 600 614 "original": { 601 - "owner": "LnL7", 602 - "ref": "nix-darwin-25.05", 615 + "owner": "nix-darwin", 616 + "ref": "nix-darwin-25.11", 603 617 "repo": "nix-darwin", 604 618 "type": "github" 605 619 } 606 620 }, 621 + "nix-index-database": { 622 + "inputs": { 623 + "nixpkgs": [ 624 + "nix-alien", 625 + "nixpkgs" 626 + ] 627 + }, 628 + "locked": { 629 + "lastModified": 1762660502, 630 + "narHash": "sha256-C9F1C31ys0V7mnp4EcDy7L1cLZw/sCTEXqqTtGnvu08=", 631 + "owner": "nix-community", 632 + "repo": "nix-index-database", 633 + "rev": "15c5451c63f4c612874a43846bfe3fa828b03eee", 634 + "type": "github" 635 + }, 636 + "original": { 637 + "owner": "nix-community", 638 + "repo": "nix-index-database", 639 + "type": "github" 640 + } 641 + }, 607 642 "nixos-hardware": { 608 643 "locked": { 609 - "lastModified": 1762847253, 610 - "narHash": "sha256-BWWnUUT01lPwCWUvS0p6Px5UOBFeXJ8jR+ZdLX8IbrU=", 644 + "lastModified": 1764440730, 645 + "narHash": "sha256-ZlJTNLUKQRANlLDomuRWLBCH5792x+6XUJ4YdFRjtO4=", 611 646 "owner": "nixos", 612 647 "repo": "nixos-hardware", 613 - "rev": "899dc449bc6428b9ee6b3b8f771ca2b0ef945ab9", 648 + "rev": "9154f4569b6cdfd3c595851a6ba51bfaa472d9f3", 614 649 "type": "github" 615 650 }, 616 651 "original": { ··· 621 656 }, 622 657 "nixpkgs": { 623 658 "locked": { 624 - "lastModified": 1763049705, 625 - "narHash": "sha256-A5LS0AJZ1yDPTa2fHxufZN++n8MCmtgrJDtxFxrH4S8=", 659 + "lastModified": 1764406085, 660 + "narHash": "sha256-CYbMp8hwuOf4umokSNp+t1s4Hjd4vxXq4S5CD+xvgNs=", 626 661 "owner": "nixos", 627 662 "repo": "nixpkgs", 628 - "rev": "3acb677ea67d4c6218f33de0db0955f116b7588c", 663 + "rev": "9561691c9f450fad7c3526916e1c4f44be0d1192", 629 664 "type": "github" 630 665 }, 631 666 "original": { 632 667 "owner": "nixos", 633 - "ref": "nixos-25.05", 668 + "ref": "nixos-25.11", 634 669 "repo": "nixpkgs", 635 670 "type": "github" 636 671 } ··· 644 679 "nixpkgs": [ 645 680 "stylix", 646 681 "nixpkgs" 647 - ], 648 - "treefmt-nix": "treefmt-nix" 682 + ] 649 683 }, 650 684 "locked": { 651 - "lastModified": 1751320053, 652 - "narHash": "sha256-3m6RMw0FbbaUUa01PNaMLoO7D99aBClmY5ed9V3vz+0=", 685 + "lastModified": 1758998580, 686 + "narHash": "sha256-VLx0z396gDCGSiowLMFz5XRO/XuNV+4EnDYjdJhHvUk=", 653 687 "owner": "nix-community", 654 688 "repo": "NUR", 655 - "rev": "cbde1735782f9c2bb2c63d5e05fba171a14a4670", 689 + "rev": "ba8d9c98f5f4630bcb0e815ab456afd90c930728", 656 690 "type": "github" 657 691 }, 658 692 "original": { ··· 661 695 "type": "github" 662 696 } 663 697 }, 664 - "pre-commit-hooks-nix": { 698 + "pre-commit": { 665 699 "inputs": { 666 - "flake-compat": [ 667 - "lanzaboote", 668 - "flake-compat" 669 - ], 700 + "flake-compat": "flake-compat", 670 701 "gitignore": "gitignore_3", 671 702 "nixpkgs": [ 672 703 "lanzaboote", ··· 674 705 ] 675 706 }, 676 707 "locked": { 677 - "lastModified": 1750779888, 678 - "narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=", 708 + "lastModified": 1765016596, 709 + "narHash": "sha256-rhSqPNxDVow7OQKi4qS5H8Au0P4S3AYbawBSmJNUtBQ=", 679 710 "owner": "cachix", 680 711 "repo": "pre-commit-hooks.nix", 681 - "rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d", 712 + "rev": "548fc44fca28a5e81c5d6b846e555e6b9c2a5a3c", 682 713 "type": "github" 683 714 }, 684 715 "original": { ··· 695 726 "home-manager": "home-manager", 696 727 "hypixel-bank-tracker": "hypixel-bank-tracker", 697 728 "lanzaboote": "lanzaboote", 729 + "nix-alien": "nix-alien", 698 730 "nix-darwin": "nix-darwin", 699 731 "nixos-hardware": "nixos-hardware", 700 732 "nixpkgs": "nixpkgs", ··· 714 746 ] 715 747 }, 716 748 "locked": { 717 - "lastModified": 1744599145, 718 - "narHash": "sha256-yzaDPkJwZdUtRj/dzdOeB74yryWzpngYaD7BedqFKk8=", 749 + "lastModified": 1764557621, 750 + "narHash": "sha256-kX5PoY8hQZ80+amMQgOO9t8Tc1JZ70gYRnzaVD4AA+o=", 719 751 "owner": "oxalica", 720 752 "repo": "rust-overlay", 721 - "rev": "fd6795d3d28f956de01a0458b6fa7baae5c793b4", 753 + "rev": "93316876c2229460a5d6f5f052766cc4cef538ce", 722 754 "type": "github" 723 755 }, 724 756 "original": { ··· 756 788 ] 757 789 }, 758 790 "locked": { 759 - "lastModified": 1761791894, 760 - "narHash": "sha256-myRIDh+PxaREz+z9LzbqBJF+SnTFJwkthKDX9zMyddY=", 791 + "lastModified": 1765075567, 792 + "narHash": "sha256-KFDCdQcHJ0hE3Nt5Gm5enRIhmtEifAjpxgUQ3mzSJpA=", 761 793 "owner": "oxalica", 762 794 "repo": "rust-overlay", 763 - "rev": "59c45eb69d9222a4362673141e00ff77842cd219", 795 + "rev": "769156779b41e8787a46ca3d7d76443aaf68be6f", 764 796 "type": "github" 765 797 }, 766 798 "original": { ··· 810 842 ] 811 843 }, 812 844 "locked": { 813 - "lastModified": 1762737305, 814 - "narHash": "sha256-5zN6jJ6KKBGiJeK3Q4+afZfJU7VyyUgehOAA3zYegTc=", 845 + "lastModified": 1764205213, 846 + "narHash": "sha256-VWKPkM4m5kGgJ0HY1WKfvlPkKka6tYwUR8snetAFTu8=", 815 847 "owner": "nix-community", 816 848 "repo": "srvos", 817 - "rev": "c04379f95fca70b38cdd45a1a7affe6d4226912b", 849 + "rev": "8b90cbaadae462563297a2d08870cccfd986ca28", 818 850 "type": "github" 819 851 }, 820 852 "original": { ··· 830 862 "base16-helix": "base16-helix", 831 863 "base16-vim": "base16-vim", 832 864 "firefox-gnome-theme": "firefox-gnome-theme", 833 - "flake-parts": "flake-parts_2", 865 + "flake-parts": "flake-parts", 834 866 "gnome-shell": "gnome-shell", 835 867 "nixpkgs": [ 836 868 "nixpkgs" 837 869 ], 838 870 "nur": "nur", 839 - "systems": "systems", 871 + "systems": "systems_2", 840 872 "tinted-foot": "tinted-foot", 841 873 "tinted-kitty": "tinted-kitty", 842 874 "tinted-schemes": "tinted-schemes", ··· 844 876 "tinted-zed": "tinted-zed" 845 877 }, 846 878 "locked": { 847 - "lastModified": 1762295027, 848 - "narHash": "sha256-5z5cGrp9F8g8iyQrM8WkB6pAwP4AaicljKZ15gx+X9Y=", 879 + "lastModified": 1764193603, 880 + "narHash": "sha256-guX30TWe8HRG2qPFiM9893F2uTw4B8D/xkE6Mq7MiYg=", 849 881 "owner": "nix-community", 850 882 "repo": "stylix", 851 - "rev": "91b9a270523361268ba6a8772152fde31103869f", 883 + "rev": "9bf8725a3d65b3ff0ba68ce13779657f5095e36b", 852 884 "type": "github" 853 885 }, 854 886 "original": { 855 887 "owner": "nix-community", 856 - "ref": "release-25.05", 888 + "ref": "release-25.11", 857 889 "repo": "stylix", 858 890 "type": "github" 859 891 } ··· 874 906 } 875 907 }, 876 908 "systems_2": { 909 + "locked": { 910 + "lastModified": 1681028828, 911 + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 912 + "owner": "nix-systems", 913 + "repo": "default", 914 + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 915 + "type": "github" 916 + }, 917 + "original": { 918 + "owner": "nix-systems", 919 + "repo": "default", 920 + "type": "github" 921 + } 922 + }, 923 + "systems_3": { 877 924 "locked": { 878 925 "lastModified": 1681028828, 879 926 "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", ··· 891 938 "tangled": { 892 939 "inputs": { 893 940 "actor-typeahead-src": "actor-typeahead-src", 894 - "flake-compat": "flake-compat_2", 941 + "flake-compat": "flake-compat_3", 895 942 "gomod2nix": "gomod2nix", 896 943 "htmx-src": "htmx-src", 897 944 "htmx-ws-src": "htmx-ws-src", ··· 905 952 "sqlite-lib-src": "sqlite-lib-src" 906 953 }, 907 954 "locked": { 908 - "lastModified": 1763089726, 909 - "narHash": "sha256-lRTZLRcqWpVf6CzJmvg+ggp/YWWasT4u2lFKIiIopoM=", 955 + "lastModified": 1764005195, 956 + "narHash": "sha256-PzuWiW/nMxwQTX0i1bHwGazQF4ptLNI9OGwpmhDb9i0=", 910 957 "ref": "refs/heads/master", 911 - "rev": "3eb9cefd98d13ab9864abb2e394fc41f89ffd923", 912 - "revCount": 1660, 958 + "rev": "7358ec6edfa4d17b8b8f543d99e83a4705901148", 959 + "revCount": 1687, 913 960 "type": "git", 914 - "url": "https://tangled.org/@tangled.org/core" 961 + "url": "https://tangled.org/tangled.org/core" 915 962 }, 916 963 "original": { 917 964 "type": "git", 918 - "url": "https://tangled.org/@tangled.org/core" 965 + "url": "https://tangled.org/tangled.org/core" 919 966 } 920 967 }, 921 968 "tinted-foot": { ··· 954 1001 "tinted-schemes": { 955 1002 "flake": false, 956 1003 "locked": { 957 - "lastModified": 1750770351, 958 - "narHash": "sha256-LI+BnRoFNRa2ffbe3dcuIRYAUcGklBx0+EcFxlHj0SY=", 1004 + "lastModified": 1757716333, 1005 + "narHash": "sha256-d4km8W7w2zCUEmPAPUoLk1NlYrGODuVa3P7St+UrqkM=", 959 1006 "owner": "tinted-theming", 960 1007 "repo": "schemes", 961 - "rev": "5a775c6ffd6e6125947b393872cde95867d85a2a", 1008 + "rev": "317a5e10c35825a6c905d912e480dfe8e71c7559", 962 1009 "type": "github" 963 1010 }, 964 1011 "original": { ··· 970 1017 "tinted-tmux": { 971 1018 "flake": false, 972 1019 "locked": { 973 - "lastModified": 1751159871, 974 - "narHash": "sha256-UOHBN1fgHIEzvPmdNMHaDvdRMgLmEJh2hNmDrp3d3LE=", 1020 + "lastModified": 1757811970, 1021 + "narHash": "sha256-n5ZJgmzGZXOD9pZdAl1OnBu3PIqD+X3vEBUGbTi4JiI=", 975 1022 "owner": "tinted-theming", 976 1023 "repo": "tinted-tmux", 977 - "rev": "bded5e24407cec9d01bd47a317d15b9223a1546c", 1024 + "rev": "d217ba31c846006e9e0ae70775b0ee0f00aa6b1e", 978 1025 "type": "github" 979 1026 }, 980 1027 "original": { ··· 986 1033 "tinted-zed": { 987 1034 "flake": false, 988 1035 "locked": { 989 - "lastModified": 1751158968, 990 - "narHash": "sha256-ksOyv7D3SRRtebpXxgpG4TK8gZSKFc4TIZpR+C98jX8=", 1036 + "lastModified": 1757811247, 1037 + "narHash": "sha256-4EFOUyLj85NRL3OacHoLGEo0wjiRJzfsXtR4CZWAn6w=", 991 1038 "owner": "tinted-theming", 992 1039 "repo": "base16-zed", 993 - "rev": "86a470d94204f7652b906ab0d378e4231a5b3384", 1040 + "rev": "824fe0aacf82b3c26690d14e8d2cedd56e18404e", 994 1041 "type": "github" 995 1042 }, 996 1043 "original": { ··· 999 1046 "type": "github" 1000 1047 } 1001 1048 }, 1002 - "treefmt-nix": { 1003 - "inputs": { 1004 - "nixpkgs": [ 1005 - "stylix", 1006 - "nur", 1007 - "nixpkgs" 1008 - ] 1009 - }, 1010 - "locked": { 1011 - "lastModified": 1733222881, 1012 - "narHash": "sha256-JIPcz1PrpXUCbaccEnrcUS8jjEb/1vJbZz5KkobyFdM=", 1013 - "owner": "numtide", 1014 - "repo": "treefmt-nix", 1015 - "rev": "49717b5af6f80172275d47a418c9719a31a78b53", 1016 - "type": "github" 1017 - }, 1018 - "original": { 1019 - "owner": "numtide", 1020 - "repo": "treefmt-nix", 1021 - "type": "github" 1022 - } 1023 - }, 1024 1049 "unixpkgs": { 1025 1050 "locked": { 1026 - "lastModified": 1762977756, 1027 - "narHash": "sha256-4PqRErxfe+2toFJFgcRKZ0UI9NSIOJa+7RXVtBhy4KE=", 1051 + "lastModified": 1764950072, 1052 + "narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=", 1028 1053 "owner": "nixos", 1029 1054 "repo": "nixpkgs", 1030 - "rev": "c5ae371f1a6a7fd27823bc500d9390b38c05fa55", 1055 + "rev": "f61125a668a320878494449750330ca58b78c557", 1031 1056 "type": "github" 1032 1057 }, 1033 1058 "original": { ··· 1069 1094 ] 1070 1095 }, 1071 1096 "locked": { 1072 - "lastModified": 1763097615, 1073 - "narHash": "sha256-qxpsf2FVzXrN0WDWRgeBz7RJ5vjHNFDy8oLqbC6gU3Y=", 1097 + "lastModified": 1764414951, 1098 + "narHash": "sha256-pZ2m2JmTTMyqiKB8WSigsSvAeoShI6OSRhzBuRO9SVY=", 1074 1099 "owner": "0xc000022070", 1075 1100 "repo": "zen-browser-flake", 1076 - "rev": "479ca480bf531285e88006aa1c70fd3bb5529f3d", 1101 + "rev": "10d2aa53ada9b14f6df2f9877d6a057f0a2b262f", 1077 1102 "type": "github" 1078 1103 }, 1079 1104 "original": {
+15 -12
flake.nix
··· 2 2 description = "NixOS and Home Manager configuration for Milo's laptops"; 3 3 4 4 inputs = { 5 - nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; 5 + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; 6 6 unixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 7 7 8 - home-manager.url = "github:nix-community/home-manager/release-25.05"; 8 + home-manager.url = "github:nix-community/home-manager/release-25.11"; 9 9 home-manager.inputs.nixpkgs.follows = "nixpkgs"; 10 10 11 - nix-darwin.url = "github:LnL7/nix-darwin/nix-darwin-25.05"; 11 + nix-darwin.url = "github:nix-darwin/nix-darwin/nix-darwin-25.11"; 12 12 nix-darwin.inputs.nixpkgs.follows = "nixpkgs"; 13 13 14 - stylix.url = "github:nix-community/stylix/release-25.05"; 14 + stylix.url = "github:nix-community/stylix/release-25.11"; 15 15 stylix.inputs.nixpkgs.follows = "nixpkgs"; 16 16 17 17 ## Miscellaneous 18 18 19 - agenix.url = "github:ryantm/agenix/0.15.0"; 19 + agenix.url = "github:ryantm/agenix"; 20 20 agenix.inputs.nixpkgs.follows = "nixpkgs"; 21 21 agenix.inputs.home-manager.follows = "home-manager"; 22 22 23 - disko.url = "github:nix-community/disko/v1.11.0"; 23 + disko.url = "github:nix-community/disko"; 24 24 disko.inputs.nixpkgs.follows = "nixpkgs"; 25 25 26 26 nixos-hardware.url = "github:nixos/nixos-hardware"; 27 27 28 - lanzaboote.url = "github:nix-community/lanzaboote/v0.4.3"; 28 + lanzaboote.url = "github:nix-community/lanzaboote"; 29 29 lanzaboote.inputs.nixpkgs.follows = "unixpkgs"; 30 30 31 31 srvos.url = "github:nix-community/srvos"; ··· 39 39 hypixel-bank-tracker.url = "github:pixilie/hypixel-bank-tracker"; 40 40 hypixel-bank-tracker.inputs.nixpkgs.follows = "nixpkgs"; 41 41 42 - tangled.url = "git+https://tangled.org/@tangled.org/core"; 42 + nix-alien.url = "github:thiagokokada/nix-alien"; 43 + nix-alien.inputs.nixpkgs.follows = "nixpkgs"; 44 + 45 + tangled.url = "git+https://tangled.org/tangled.org/core"; 43 46 tangled.inputs.nixpkgs.follows = "unixpkgs"; 44 47 45 48 wakatime-ls.url = "github:mrnossiom/wakatime-ls"; ··· 56 59 inherit (flake-lib) forAllSystems; 57 60 58 61 flake-lib = import ./lib/flake (nixpkgs // { inherit self; }); 59 - 60 - forAllPkgs = func: forAllSystems (system: func pkgs.${system}); 61 62 62 63 # This should be the only constructed nixpkgs instances in this flake 63 - pkgs = forAllSystems (system: (import nixpkgs { 64 + allPkgs = forAllSystems (system: (import nixpkgs { 64 65 inherit system; 65 66 config.allowUnfreePredicate = import ./lib/unfree.nix { lib = nixpkgs.lib; }; 66 67 overlays = [ outputs.overlays.all ]; 67 68 })); 69 + 70 + forAllPkgs = func: forAllSystems (system: func allPkgs.${system}); 68 71 in 69 72 { 70 73 formatter = forAllPkgs (pkgs: pkgs.nixpkgs-fmt); ··· 73 76 lib = forAllPkgs (import ./lib); 74 77 templates = import ./templates; 75 78 76 - apps = forAllPkgs (import ./apps { pkgs-per-system = pkgs; }); 79 + apps = forAllPkgs (import ./apps { pkgs-per-system = allPkgs; }); 77 80 devShells = forAllPkgs (import ./shells.nix); 78 81 overlays = import ./overlays (nixpkgs // { inherit self; }); 79 82 packages = forAllPkgs (import ./pkgs);
+4
home-manager/fragments/firefox.nix
··· 84 84 enable = false; 85 85 profileNames = [ "default" ]; 86 86 }; 87 + stylix.targets.zen-browser = { 88 + enable = false; 89 + profileNames = [ "default" ]; 90 + }; 87 91 88 92 programs.zen-browser = { 89 93 enable = true;
+57 -53
home-manager/fragments/git.nix
··· 15 15 ''; 16 16 17 17 config = lib.mkIf cfg.enable { 18 - assertions = [ 19 - { assertion = config.local.fragment.agenix.enable; message = "`git` fragment depends on `agenix` fragment"; } 20 - ]; 21 - 22 18 home.sessionVariables = { 23 19 # Disable annoying warning message 24 20 GIT_DISCOVERY_ACROSS_FILESYSTEM = 0; ··· 28 24 enable = true; 29 25 lfs.enable = true; 30 26 31 - userName = "Milo Moisson"; 32 - # TODO: this email should be behind a secret or at least a config 33 - userEmail = "milo@wiro.world"; 34 - 35 27 signing.signByDefault = true; 36 28 signing.key = "~/.ssh/id_ed25519.pub"; 37 29 ··· 43 35 "result" 44 36 ]; 45 37 46 - aliases = { 47 - b = "branch --all"; 48 - brm = "branch --delete"; 38 + settings = { 39 + user = { 40 + name = "Milo Moisson"; 41 + # TODO: this email should be behind a secret or at least a config 42 + email = "milo@wiro.world"; 43 + }; 49 44 50 - ll = "log --graph --oneline --pretty=custom"; 51 - lla = "log --graph --oneline --pretty=custom --all"; 52 - last = "log -1 HEAD --stat"; 45 + alias = { 46 + b = "branch --all"; 47 + brm = "branch --delete"; 53 48 54 - st = "status --short --branch"; 49 + ll = "log --graph --oneline --pretty=custom"; 50 + lla = "log --graph --oneline --pretty=custom --all"; 51 + last = "log -1 HEAD --stat"; 55 52 56 - cm = "commit --message"; 57 - oups = "commit --amend"; 53 + st = "status --short --branch"; 58 54 59 - ui = "!lazygit"; 55 + cm = "commit --message"; 56 + oups = "commit --amend"; 60 57 61 - rv = "remote --verbose"; 58 + ui = "!lazygit"; 62 59 63 - ri = "rebase --interactive"; 64 - ris = "!git ri $(git slc)"; 65 - rc = "rebase --continue"; 66 - rs = "rebase --skip"; 67 - ra = "rebase --abort"; 60 + rv = "remote --verbose"; 68 61 69 - # Select commit 70 - slc = "!git log --oneline --pretty=custom | fzf | awk '{printf $1}'"; 62 + ri = "rebase --interactive"; 63 + ris = "!git ri $(git slc)"; 64 + rc = "rebase --continue"; 65 + rs = "rebase --skip"; 66 + ra = "rebase --abort"; 71 67 72 - a = "add"; 73 - al = "add --all"; 74 - ac = "add ."; 75 - ap = "add --patch"; 68 + # Select commit 69 + slc = "!git log --oneline --pretty=custom | fzf | awk '{printf $1}'"; 76 70 77 - pu = "push"; 78 - put = "push --follow-tags"; 79 - puf = "push --force-with-lease"; 80 - pl = "pull"; 71 + a = "add"; 72 + al = "add --all"; 73 + ac = "add ."; 74 + ap = "add --patch"; 81 75 82 - f = "fetch"; 76 + pu = "push"; 77 + put = "push --follow-tags"; 78 + puf = "push --force-with-lease"; 79 + pl = "pull"; 83 80 84 - s = "switch"; 85 - sc = "switch --create"; 81 + f = "fetch"; 86 82 87 - ck = "checkout"; 83 + s = "switch"; 84 + sc = "switch --create"; 88 85 89 - cp = "cherry-pick"; 86 + ck = "checkout"; 90 87 91 - df = "diff"; 92 - dfs = "diff --staged"; 93 - dfc = "diff --cached"; 88 + cp = "cherry-pick"; 89 + 90 + df = "diff"; 91 + dfs = "diff --staged"; 92 + dfc = "diff --cached"; 94 93 95 - m = "merge"; 94 + m = "merge"; 96 95 97 - rms = "restore --staged"; 98 - res = "restore"; 96 + rms = "restore --staged"; 97 + res = "restore"; 99 98 100 - sh = "stash"; 101 - shl = "stash list"; 102 - sha = "stash apply"; 103 - shp = "stash pop"; 104 - }; 99 + sh = "stash"; 100 + shl = "stash list"; 101 + sha = "stash apply"; 102 + shp = "stash pop"; 103 + }; 105 104 106 - extraConfig = { 107 105 fetch.prune = true; 108 106 color.ui = true; 109 107 init.defaultBranch = "main"; ··· 131 129 "credentials \"https://github.com\"".helper = "!${lib.getExe pkgs.gh} auth git-credential"; 132 130 133 131 # TODO: change to $PROJECTS env var? 134 - leaveTool.defaultFolder = "~/Development"; 132 + leaveTool = { 133 + defaultFolder = "${config.home.homeDirectory}/Development"; 134 + checks = [ "dirty" "ahead-branches" ]; 135 + }; 135 136 }; 136 137 }; 137 138 ··· 158 159 showCommandLog = false; 159 160 border = "single"; 160 161 }; 162 + 161 163 git = { 162 - paging.externalDiffCommand = "difft --color=always"; 164 + pagers = [ 165 + { externalDiffCommand = "difft --color=always"; } 166 + ]; 163 167 }; 164 168 165 169 # to be declarative or not to be declarative?
-1
home-manager/fragments/helix.nix
··· 93 93 }; 94 94 95 95 extraPackages = with pkgs; [ 96 - ansible-language-server 97 96 clang-tools 98 97 gopls 99 98 kotlin-language-server
+2 -2
home-manager/fragments/jujutsu.nix
··· 29 29 signing = { 30 30 behavior = "own"; 31 31 backend = "ssh"; 32 - key = keys.milomoisson; 33 - 32 + key = keys.milo-ed25519; 34 33 git.sign-on-push = true; 35 34 }; 36 35 ··· 63 62 64 63 templates = { 65 64 log = "custom_log_compact"; 65 + git_push_bookmark = ''"push-" ++ change_id.short()''; 66 66 }; 67 67 68 68 ui = {
+1 -3
home-manager/fragments/kitty.nix
··· 15 15 Depends on: 16 16 - (Darwin) `fish` program: launches fish on startup 17 17 18 - Has weird behavior if set as login shell 18 + fish has weird behavior if set as login shell 19 19 ''; 20 20 21 21 config = lib.mkIf cfg.enable { ··· 38 38 }; 39 39 }; 40 40 } 41 - 42 -
+14 -10
home-manager/fragments/shell.nix
··· 29 29 git_status.disabled = true; 30 30 31 31 nix_shell = { 32 - format = "via [$symbol$state]($style) "; # Remove nix shell name 32 + # remove nix shell name 33 + format = "via [$symbol]($style) "; 33 34 symbol = " "; 34 35 }; 35 36 }; ··· 38 39 programs.direnv = { 39 40 enable = true; 40 41 silent = true; 42 + 41 43 nix-direnv.enable = true; 42 - }; 43 44 44 - programs.nushell = { 45 - enable = true; 46 - 47 - extraConfig = '' 48 - $env.config = { 49 - show_banner: false, 50 - } 45 + stdlib = '' 46 + use angrr 51 47 ''; 52 48 }; 49 + # TODO: depend on osConfig 50 + xdg.configFile."direnv/lib/angrr.sh".text = '' 51 + use_angrr() { 52 + layout_dir="$(direnv_layout_dir)" 53 + log_status "angrr: touch GC roots $layout_dir" 54 + RUST_LOG="''${ANGRR_DIRENV_LOG:-angrr=error}" ${lib.getExe upkgs.angrr} touch "$layout_dir" --silent 55 + } 56 + ''; 53 57 54 58 programs.zoxide = { 55 59 enable = true; ··· 147 151 ''; 148 152 149 153 # Quickly explore a derivation (using registry syntax) 150 - # e.g. `cdd nixpkgs#fontforge` or `cdd unixpkgs#fontforge` 154 + # e.g. `cdd nixpkgs#fontforge` or `cdd unixpkgs#fontforge` 151 155 cdd = "cd (nix build --no-link --print-out-paths $argv | ${lib.getExe pkgs.fzf})"; 152 156 } // lib.optionalAttrs (!flags.onlyCached) { 153 157 # Quickly get outta here to test something
+5 -1
home-manager/fragments/stylix.nix
··· 11 11 cfg = config.local.fragment.stylix; 12 12 in 13 13 { 14 - imports = [ stylix.homeModules.stylix ]; 14 + imports = [ 15 + stylix.homeModules.stylix 16 + # issues a warning because we use `useGlobalPkgs` 17 + { config.stylix.overlays.enable = false; } 18 + ]; 15 19 16 20 options.local.fragment.stylix.enable = lib.mkEnableOption '' 17 21 Stylix related
+94 -88
home-manager/fragments/sway.nix
··· 1 - { self 2 - , config 1 + { config 3 2 , lib 4 3 , pkgs 5 4 ··· 8 7 }: 9 8 10 9 let 11 - inherit (self.outputs) homeManagerModules; 12 - 13 10 cfg = config.local.fragment.sway; 14 11 cfg-sway = config.wayland.windowManager.sway.config; 15 12 16 13 workspacesRange = lib.zipListsWith (key-idx: workspace-idx: { inherit key-idx workspace-idx; }) [ 1 2 3 4 5 6 7 8 9 0 ] (lib.range 1 10); 17 14 in 18 15 { 19 - imports = [ 20 - homeManagerModules.wl-clip-persist 21 - ]; 22 - 23 16 options.local.fragment.sway.enable = lib.mkEnableOption '' 24 17 Sway related 25 18 ''; ··· 115 108 xwayland = true; # explicit, op is true by default 116 109 117 110 config = { 118 - modifier = "Mod4"; # Super key 111 + modifier = "Super"; 119 112 terminal = config.home.sessionVariables.TERMINAL; 120 113 121 114 defaultWorkspace = "workspace number 1"; ··· 171 164 repeat_rate = toString 30; 172 165 }; 173 166 167 + "type:touchpad" = { 168 + dwt = "enabled"; 169 + tap = "enabled"; 170 + tap_button_map = "lrm"; 171 + }; 172 + 174 173 # Split keyboard also acts as a mouse 175 174 # "type:touchpad" = { events = "disabled_on_external_mouse"; }; 176 175 ··· 188 187 bindkeysToCode = true; 189 188 keybindings = 190 189 let 190 + mod = cfg-sway.modifier; 191 + 191 192 pamixer = lib.getExe pkgs.pamixer; 192 193 playerctl = lib.getExe pkgs.playerctl; 194 + loginctl = lib.getExe' pkgs.systemd "loginctl"; 193 195 brightnessctl = lib.getExe pkgs.brightnessctl; 196 + nautilus = lib.getExe pkgs.nautilus; 194 197 makoctl = lib.getExe' pkgs.mako "makoctl"; 195 198 196 199 grim = lib.getExe pkgs.grim; 197 200 slurp = lib.getExe pkgs.slurp; 201 + swappy = lib.getExe pkgs.swappy; 198 202 wl-copy = lib.getExe' pkgs.wl-clipboard "wl-copy"; 199 203 wl-paste = lib.getExe' pkgs.wl-clipboard "wl-paste"; 200 204 in 201 - lib.foldl (acc: val: acc // val) { } 202 - (map 203 - (modifier: { 204 - "${modifier}+Return" = "exec ${cfg-sway.terminal}"; 205 - "${modifier}+Shift+Return" = "exec ${lib.getExe' pkgs.nautilus "nautilus"}"; 206 - "${modifier}+Shift+q" = "kill"; 207 - "${modifier}+d" = "exec ${cfg-sway.menu}"; 208 - "${modifier}+Space" = "exec ${makoctl} dismiss"; 205 + { 206 + "${mod}+Return" = "exec ${cfg-sway.terminal}"; 207 + "${mod}+Shift+Return" = "exec ${nautilus}"; 208 + "${mod}+Shift+q" = "kill"; 209 + "${mod}+d" = "exec ${cfg-sway.menu}"; 210 + "${mod}+Space" = "exec ${makoctl} dismiss"; 209 211 210 - "${modifier}+Escape" = "exec ${lib.getExe' pkgs.systemd "loginctl"} lock-session"; 211 - "${modifier}+Alt+Escape" = "exec ${pkgs.writeShellScript "lock-screenshot.sh" '' 212 - tmpimg=$(${lib.getExe' pkgs.coreutils "mktemp"} /tmp/lock-bg.XXX) 212 + "${mod}+Escape" = "exec ${loginctl} lock-session"; 213 + "${mod}+Alt+Escape" = "exec ${pkgs.writeShellScript "lock-screenshot.sh" '' 214 + tmpimg=$(${lib.getExe' pkgs.coreutils "mktemp"} /tmp/lock-bg.XXX) 213 215 214 - # Give some time to hide the bar 215 - sleep 1 216 + # Give some time to hide the bar 217 + sleep 1 216 218 217 - ${grim} $tmpimg 218 - ${lib.getExe pkgs.swaylock} --image $tmpimg 219 + ${grim} $tmpimg 220 + ${lib.getExe pkgs.swaylock} --image $tmpimg 219 221 220 - rm $tmpimg 221 - ''}"; 222 + rm $tmpimg 223 + ''}"; 222 224 223 - "${modifier}+t" = ''input "type:touch" events toggle''; 225 + "${mod}+t" = ''input "type:touch" events toggle''; 224 226 225 - "${modifier}+${cfg-sway.left}" = "focus left"; 226 - "${modifier}+${cfg-sway.down}" = "focus down"; 227 - "${modifier}+${cfg-sway.up}" = "focus up"; 228 - "${modifier}+${cfg-sway.right}" = "focus right"; 227 + "${mod}+${cfg-sway.left}" = "focus left"; 228 + "${mod}+${cfg-sway.down}" = "focus down"; 229 + "${mod}+${cfg-sway.up}" = "focus up"; 230 + "${mod}+${cfg-sway.right}" = "focus right"; 229 231 230 - "${modifier}+Shift+${cfg-sway.left}" = "move left"; 231 - "${modifier}+Shift+${cfg-sway.down}" = "move down"; 232 - "${modifier}+Shift+${cfg-sway.up}" = "move up"; 233 - "${modifier}+Shift+${cfg-sway.right}" = "move right"; 234 - "${modifier}+b" = "split vertical"; 235 - "${modifier}+n" = "split horizontal"; 232 + "${mod}+Shift+${cfg-sway.left}" = "move left"; 233 + "${mod}+Shift+${cfg-sway.down}" = "move down"; 234 + "${mod}+Shift+${cfg-sway.up}" = "move up"; 235 + "${mod}+Shift+${cfg-sway.right}" = "move right"; 236 + "${mod}+b" = "split vertical"; 237 + "${mod}+n" = "split horizontal"; 236 238 237 - "${modifier}+Alt+${cfg-sway.left}" = "resize shrink width 60 px"; 238 - "${modifier}+Alt+${cfg-sway.down}" = "resize grow height 60 px"; 239 - "${modifier}+Alt+${cfg-sway.up}" = "resize shrink height 60 px"; 240 - "${modifier}+Alt+${cfg-sway.right}" = "resize grow width 60 px"; 241 - "${modifier}+f" = "fullscreen toggle"; 242 - "${modifier}+Shift+space" = "floating toggle"; 243 - # Change between tiling and floating focus 244 - "${modifier}+Alt+space" = "focus mode_toggle"; 245 - "${modifier}+Alt+c" = "move position cursor"; 246 - "${modifier}+p" = "sticky toggle"; 239 + "${mod}+Alt+${cfg-sway.left}" = "resize shrink width 60 px"; 240 + "${mod}+Alt+${cfg-sway.down}" = "resize grow height 60 px"; 241 + "${mod}+Alt+${cfg-sway.up}" = "resize shrink height 60 px"; 242 + "${mod}+Alt+${cfg-sway.right}" = "resize grow width 60 px"; 243 + "${mod}+f" = "fullscreen toggle"; 244 + "${mod}+Shift+space" = "floating toggle"; 245 + # Change between tiling and floating focus 246 + "${mod}+Alt+space" = "focus mode_toggle"; 247 + "${mod}+Alt+c" = "move position cursor"; 248 + "${mod}+p" = "sticky toggle"; 247 249 248 - # Screenshotting 249 - "${modifier}+s" = ''exec ${grim} -g "$(${slurp})" - | ${wl-copy}''; 250 - "${modifier}+Shift+s" = "exec ${wl-paste} | ${lib.getExe pkgs.swappy} --file - --output-file - | ${wl-copy}"; 250 + # Screenshotting 251 + "${mod}+s" = ''exec ${grim} -g "$(${slurp})" - | ${wl-copy}''; 252 + "${mod}+Shift+s" = "exec ${wl-paste} | ${swappy} --file - --output-file - | ${wl-copy}"; 251 253 252 - # Soundcontrol Keys 253 - "--locked XF86AudioPrev" = "exec ${playerctl} previous"; 254 - "--locked XF86AudioNext" = "exec ${playerctl} next"; 255 - "--locked XF86AudioPlay" = "exec ${playerctl} play-pause"; 256 - "--locked XF86AudioStop" = "exec ${playerctl} stop"; 257 - "--locked XF86AudioRaiseVolume" = "exec ${pamixer} --unmute --increase 5"; 258 - "--locked XF86AudioLowerVolume" = "exec ${pamixer} --unmute --decrease 5"; 259 - "--locked XF86AudioMute" = "exec ${pamixer} --toggle-mute"; 260 - "--locked XF86AudioMicMute" = "exec ${pamixer} --default-source --toggle-mute"; 261 - "--locked XF86MonBrightnessUp" = "exec ${brightnessctl} --exponent set 5%+"; 262 - "--locked XF86MonBrightnessDown" = "exec ${brightnessctl} --exponent set 5%- --min-value=1"; 263 - "--locked XF86TouchpadToggle" = ''input "type:touchpad" events toggle enabled disabled_on_external_mouse''; 264 - } 265 - // lib.listToAttrs (lib.flatten (map 266 - ({ key-idx, workspace-idx }: [ 267 - { name = "${modifier}+${toString key-idx}"; value = "workspace number ${toString workspace-idx}"; } 268 - { name = "${modifier}+Alt+${toString key-idx}"; value = "move container to workspace number ${toString workspace-idx}"; } 269 - { name = "${modifier}+Shift+${toString key-idx}"; value = "move container to workspace number ${toString workspace-idx}; workspace number ${toString workspace-idx}"; } 270 - ]) 271 - workspacesRange)) 272 - ) [ cfg-sway.modifier ]); 254 + # Soundcontrol Keys 255 + "--locked XF86AudioPrev" = "exec ${playerctl} previous"; 256 + "--locked XF86AudioNext" = "exec ${playerctl} next"; 257 + "--locked XF86AudioPlay" = "exec ${playerctl} play-pause"; 258 + "--locked XF86AudioStop" = "exec ${playerctl} stop"; 259 + "--locked XF86AudioRaiseVolume" = "exec ${pamixer} --unmute --increase 5"; 260 + "--locked XF86AudioLowerVolume" = "exec ${pamixer} --unmute --decrease 5"; 261 + "--locked XF86AudioMute" = "exec ${pamixer} --toggle-mute"; 262 + "--locked XF86AudioMicMute" = "exec ${pamixer} --default-source --toggle-mute"; 263 + "--locked XF86MonBrightnessUp" = "exec ${brightnessctl} --exponent set 5%+"; 264 + "--locked XF86MonBrightnessDown" = "exec ${brightnessctl} --exponent set 5%- --min-value=1"; 265 + "--locked XF86TouchpadToggle" = ''input "type:touchpad" events toggle enabled disabled_on_external_mouse''; 266 + } 267 + // lib.listToAttrs (lib.flatten (map 268 + ({ key-idx, workspace-idx }: [ 269 + { name = "${mod}+${toString key-idx}"; value = "workspace number ${toString workspace-idx}"; } 270 + { name = "${mod}+Alt+${toString key-idx}"; value = "move container to workspace number ${toString workspace-idx}"; } 271 + { name = "${mod}+Shift+${toString key-idx}"; value = "move container to workspace number ${toString workspace-idx}; workspace number ${toString workspace-idx}"; } 272 + ]) 273 + workspacesRange)); 273 274 }; 274 275 }; 275 276 ··· 277 278 278 279 services.poweralertd.enable = true; 279 280 280 - services.darkman = { 281 - enable = true; 282 - settings.usegeoclue = true; 281 + services.darkman = 282 + let 283 + dconf = lib.getExe pkgs.dconf; 284 + brightnessctl = lib.getExe pkgs.brightnessctl; 285 + in 286 + { 287 + enable = true; 288 + settings.usegeoclue = true; 283 289 284 - darkModeScripts.gtk-theme = '' 285 - # Change system theme scheme to dark 286 - ${lib.getExe pkgs.dconf} write /org/gnome/desktop/interface/color-scheme "'prefer-dark'" 290 + darkModeScripts.gtk-theme = '' 291 + # Change system theme scheme to dark 292 + ${dconf} write /org/gnome/desktop/interface/color-scheme "'prefer-dark'" 287 293 288 - # Do not change brightness as I'm usually on my computer as this time 289 - ''; 294 + # Do not change brightness as I'm usually on my computer as this time 295 + ''; 290 296 291 - lightModeScripts.gtk-theme = '' 292 - # Change system theme scheme to light 293 - ${lib.getExe pkgs.dconf} write /org/gnome/desktop/interface/color-scheme "'prefer-light'" 297 + lightModeScripts.gtk-theme = '' 298 + # Change system theme scheme to light 299 + ${dconf} write /org/gnome/desktop/interface/color-scheme "'prefer-light'" 294 300 295 - # TODO: change config specialization 301 + # TODO: change config specialization 296 302 297 - # Prepare laptop for wake: set full brightness and disable kbd backlight 298 - ${lib.getExe pkgs.brightnessctl} --class backlight set 100% 299 - ${lib.getExe pkgs.brightnessctl} --class leds --device "*::kbd_backlight" set 0% 300 - ''; 301 - }; 303 + # Prepare laptop for wake: set full brightness and disable kbd backlight 304 + ${brightnessctl} --class backlight set 100% 305 + ${brightnessctl} --class leds --device "*::kbd_backlight" set 0% 306 + ''; 307 + }; 302 308 303 309 services.gammastep = { 304 310 enable = true;
+7 -3
home-manager/fragments/tools.nix
··· 1 1 { config 2 2 , lib 3 3 , pkgs 4 + , lpkgs 4 5 , ... 5 6 }: 6 7 ··· 29 30 csvlens 30 31 delta 31 32 dogdns 32 - du-dust 33 + dust 33 34 encfs 34 35 fastfetch 35 36 fd 36 37 ffmpeg 37 38 file 38 39 fzf 40 + gemini-cli 39 41 inetutils 40 42 jq 41 43 just ··· 47 49 otree 48 50 ouch 49 51 parallel 52 + perf 50 53 pv 51 54 restic 52 55 ripgrep ··· 60 63 uni 61 64 unzip 62 65 vlock 63 - wcurl 64 66 wormhole-rs 65 - ]) ++ lib.optionals (!flags.onlyCached) [ ]; 67 + ]) ++ lib.optionals (!flags.onlyCached) [ 68 + lpkgs.nix-alien 69 + ]; 66 70 67 71 programs.fish.shellAbbrs = { 68 72 # Use newer tools
+1 -1
home-manager/fragments/waybar.nix
··· 232 232 .modules-right widget .module { 233 233 padding: 0 1rem; 234 234 235 - color: @base07; 235 + color: @base07; 236 236 } 237 237 238 238 /* Round first and last child of left, right and center modules. Disable rounding on the sides*/
+8 -1
home-manager/profiles/desktop.nix
··· 95 95 transmission_4-gtk 96 96 wdisplays 97 97 wireshark 98 + zulip 98 99 99 100 # Needed for libreoffice spellchecking 100 101 hunspell ··· 122 123 123 124 programs.broot.enable = true; 124 125 126 + stylix.targets.qt.enable = false; 127 + 125 128 programs.go = { 126 129 enable = true; 127 - goPath = ".local/share/go"; 130 + env.GOPATH = "${config.home.homeDirectory}/.local/share/go"; 128 131 }; 129 132 130 133 programs.gpg = { ··· 138 141 enableFishIntegration = false; 139 142 enableZshIntegration = false; 140 143 }; 144 + 145 + programs.ssh.enableDefaultConfig = false; 146 + 147 + services.tailscale-systray.enable = true; 141 148 }; 142 149 }
+163
hosts/weird-row-server/authelia.nix
··· 1 + { config 2 + , ... 3 + }: 4 + 5 + let 6 + authelia-port = 3008; 7 + authelia-hostname = "auth.wiro.world"; 8 + 9 + authelia-metrics-port = 9004; 10 + headscale-hostname = "headscale.wiro.world"; 11 + grafana-hostname = "console.wiro.world"; 12 + miniflux-hostname = "news.wiro.world"; 13 + in 14 + { 15 + config = { 16 + age.secrets.authelia-jwt-secret = { file = secrets/authelia-jwt-secret.age; owner = config.services.authelia.instances.main.user; }; 17 + age.secrets.authelia-issuer-private-key = { file = secrets/authelia-issuer-private-key.age; owner = config.services.authelia.instances.main.user; }; 18 + age.secrets.authelia-storage-key = { file = secrets/authelia-storage-key.age; owner = config.services.authelia.instances.main.user; }; 19 + age.secrets.authelia-ldap-password = { file = secrets/authelia-ldap-password.age; owner = config.services.authelia.instances.main.user; }; 20 + age.secrets.authelia-smtp-password = { file = secrets/authelia-smtp-password.age; owner = config.services.authelia.instances.main.user; }; 21 + services.authelia.instances.main = { 22 + enable = true; 23 + 24 + secrets = { 25 + jwtSecretFile = config.age.secrets.authelia-jwt-secret.path; 26 + oidcIssuerPrivateKeyFile = config.age.secrets.authelia-issuer-private-key.path; 27 + storageEncryptionKeyFile = config.age.secrets.authelia-storage-key.path; 28 + }; 29 + environmentVariables = { 30 + AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE = config.age.secrets.authelia-ldap-password.path; 31 + AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = config.age.secrets.authelia-smtp-password.path; 32 + }; 33 + settings = { 34 + server.address = "localhost:${toString authelia-port}"; 35 + storage.local.path = "/var/lib/authelia-main/db.sqlite3"; 36 + telemetry.metrics = { 37 + enabled = true; 38 + address = "tcp://:${toString authelia-metrics-port}/metrics"; 39 + }; 40 + 41 + session = { 42 + cookies = [{ 43 + domain = "wiro.world"; 44 + authelia_url = "https://${authelia-hostname}"; 45 + default_redirection_url = "https://wiro.world"; 46 + }]; 47 + }; 48 + 49 + authentication_backend.ldap = { 50 + address = "ldap://localhost:3890"; 51 + timeout = "5m"; # replace with systemd dependency 52 + 53 + user = "uid=authelia,ou=people,dc=wiro,dc=world"; 54 + # Set in `AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE`. 55 + # password = ""; 56 + 57 + base_dn = "dc=wiro,dc=world"; 58 + users_filter = "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person))"; 59 + additional_users_dn = "ou=people"; 60 + groups_filter = "(&(member={dn})(objectClass=groupOfNames))"; 61 + additional_groups_dn = "ou=groups"; 62 + 63 + attributes = { 64 + username = "uid"; 65 + display_name = "cn"; 66 + given_name = "givenname"; 67 + family_name = "last_name"; 68 + mail = "mail"; 69 + picture = "avatar"; 70 + 71 + group_name = "cn"; 72 + }; 73 + }; 74 + 75 + access_control = { 76 + default_policy = "deny"; 77 + # Rules are sequential and do not apply to OIDC 78 + rules = [ 79 + { 80 + domain = "headscale.wiro.world"; 81 + policy = "two_factor"; 82 + 83 + } 84 + { 85 + domain = "news.wiro.world"; 86 + policy = "one_factor"; 87 + 88 + subject = [ [ "group:miniflux" "oauth2:client:miniflux" ] ]; 89 + } 90 + { 91 + domain = "*.wiro.world"; 92 + policy = "two_factor"; 93 + } 94 + ]; 95 + }; 96 + 97 + identity_providers.oidc = { 98 + enforce_pkce = "always"; 99 + 100 + authorization_policies = 101 + let 102 + mkStrictPolicy = policy: subject: 103 + { default_policy = "deny"; rules = [{ inherit policy subject; }]; }; 104 + in 105 + { 106 + headscale = mkStrictPolicy "two_factor" [ "group:headscale" ]; 107 + tailscale = mkStrictPolicy "two_factor" [ "group:headscale" ]; 108 + grafana = mkStrictPolicy "one_factor" [ "group:grafana" ]; 109 + miniflux = mkStrictPolicy "one_factor" [ "group:miniflux" ]; 110 + }; 111 + 112 + claims_policies.headscale = { id_token = [ "email" "name" "preferred_username" "picture" "groups" ]; }; 113 + 114 + clients = [ 115 + { 116 + client_name = "Headscale"; 117 + client_id = "headscale"; 118 + client_secret = "$pbkdf2-sha256$310000$XY680D9gkSoWhD0UtYHNFg$ptWB3exOYCga6uq1N.oimuV3ILjK3F8lBWBpsBpibos"; 119 + redirect_uris = [ "https://${headscale-hostname}/oidc/callback" ]; 120 + authorization_policy = "headscale"; 121 + claims_policy = "headscale"; 122 + } 123 + { 124 + client_name = "Tailscale"; 125 + client_id = "tailscale"; 126 + client_secret = "$pbkdf2-sha256$310000$PcUaup9aWKI9ZLeCF6.avw$FpsTxkDaxcoQlBi8aIacegXpjEDiCI6nXcaHyZ2Sxyc"; 127 + redirect_uris = [ "https://login.tailscale.com/a/oauth_response" ]; 128 + authorization_policy = "tailscale"; 129 + } 130 + { 131 + client_name = "Grafana Console"; 132 + client_id = "grafana"; 133 + client_secret = "$pbkdf2-sha256$310000$UkwrqxTZodGMs9.Ca2cXAA$HCWFgQbFHGXZpuz.I3HHdkTZLUevRVGlhKEFaOlPmKs"; 134 + redirect_uris = [ "https://${grafana-hostname}/login/generic_oauth" ]; 135 + authorization_policy = "grafana"; 136 + } 137 + { 138 + client_name = "Miniflux"; 139 + client_id = "miniflux"; 140 + client_secret = "$pbkdf2-sha256$310000$uPqbWfCOBXDY6nV1vsx3uA$HOWG2hL.c/bs9Dwaee3b9DxjH7KFO.SaZMbasXV9Vdw"; 141 + redirect_uris = [ "https://${miniflux-hostname}/oauth2/oidc/callback" ]; 142 + authorization_policy = "miniflux"; 143 + } 144 + ]; 145 + }; 146 + 147 + notifier.smtp = { 148 + address = "smtp://smtp.resend.com:2587"; 149 + username = "resend"; 150 + # Set in `AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE`. 151 + # password = ""; 152 + sender = "authelia@wiro.world"; 153 + }; 154 + }; 155 + }; 156 + 157 + services.caddy = { 158 + virtualHosts.${authelia-hostname}.extraConfig = '' 159 + reverse_proxy http://localhost:${toString authelia-port} 160 + ''; 161 + }; 162 + }; 163 + }
+144
hosts/weird-row-server/default.nix
··· 1 + { self 2 + , config 3 + , pkgs 4 + , ... 5 + }: 6 + 7 + let 8 + inherit (self.inputs) srvos; 9 + 10 + ext-if = "eth0"; 11 + external-ip = "91.99.55.74"; 12 + external-netmask = 27; 13 + external-gw = "144.x.x.255"; 14 + external-ip6 = "2a01:4f8:c2c:76d2::1"; 15 + external-netmask6 = 64; 16 + external-gw6 = "fe80::1"; 17 + 18 + website-hostname = "wiro.world"; 19 + 20 + static-hostname = "static.wiro.world"; 21 + in 22 + { 23 + imports = [ 24 + srvos.nixosModules.server 25 + srvos.nixosModules.hardware-hetzner-cloud 26 + srvos.nixosModules.mixins-terminfo 27 + 28 + ./authelia.nix 29 + ./goatcounter.nix 30 + ./grafana.nix 31 + ./headscale.nix 32 + ./hypixel-bank-tracker.nix 33 + ./lldap.nix 34 + ./miniflux.nix 35 + ./paisa.nix 36 + ./pds.nix 37 + ./tangled.nix 38 + ./thelounge.nix 39 + ./tuwunel.nix 40 + ./vaultwarden.nix 41 + ./warrior.nix 42 + ./webfinger.nix 43 + ]; 44 + 45 + config = { 46 + boot.loader.grub.enable = true; 47 + boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" "ext4" ]; 48 + 49 + # Single network card is `eth0` 50 + networking.usePredictableInterfaceNames = false; 51 + 52 + networking.nameservers = [ "2001:4860:4860::8888" "2001:4860:4860::8844" ]; 53 + 54 + networking = { 55 + interfaces.${ext-if} = { 56 + ipv4.addresses = [{ address = external-ip; prefixLength = external-netmask; }]; 57 + ipv6.addresses = [{ address = external-ip6; prefixLength = external-netmask6; }]; 58 + }; 59 + defaultGateway = { interface = ext-if; address = external-gw; }; 60 + defaultGateway6 = { interface = ext-if; address = external-gw6; }; 61 + 62 + # Reflect firewall configuration on Hetzner 63 + firewall.allowedTCPPorts = [ 22 80 443 ]; 64 + }; 65 + 66 + services.qemuGuest.enable = true; 67 + 68 + services.openssh.enable = true; 69 + 70 + # age.secrets.tailscale-authkey.file = secrets/tailscale-authkey.age; 71 + services.tailscale = { 72 + enable = true; 73 + extraSetFlags = [ "--advertise-exit-node" ]; 74 + # authKeyFile = config.age.secrets.tailscale-authkey.path; 75 + authKeyParameters = { 76 + baseURL = "https://headscale.wiro.world"; 77 + ephemeral = true; 78 + preauthorized = true; 79 + }; 80 + }; 81 + 82 + security.sudo.wheelNeedsPassword = false; 83 + 84 + local.fragment.nix.enable = true; 85 + 86 + programs.fish.enable = true; 87 + 88 + services.fail2ban = { 89 + enable = true; 90 + 91 + maxretry = 5; 92 + ignoreIP = [ ]; 93 + 94 + bantime = "24h"; 95 + bantime-increment = { 96 + enable = true; 97 + multipliers = "1 2 4 8 16 32 64"; 98 + maxtime = "168h"; 99 + overalljails = true; 100 + }; 101 + 102 + jails = { }; 103 + }; 104 + 105 + age.secrets.caddy-env.file = secrets/caddy-env.age; 106 + services.caddy = { 107 + enable = true; 108 + package = pkgs.caddy.withPlugins { 109 + plugins = [ 110 + "github.com/caddy-dns/hetzner/v2@v2.0.0-preview-1" 111 + "github.com/tailscale/caddy-tailscale@v0.0.0-20251016213337-01d084e119cb" 112 + ]; 113 + hash = "sha256-muKwDYs5Jp4ib/psZxpp1Kyfsqz6wPz/lpHFGtx67uY="; 114 + }; 115 + 116 + environmentFile = config.age.secrets.caddy-env.path; 117 + 118 + globalConfig = '' 119 + tailscale { 120 + # this caddy instance already proxies headscale but needs to access headscale to start 121 + # control_url https://headscale.wiro.world 122 + control_url http://localhost:3006 123 + 124 + ephemeral 125 + } 126 + ''; 127 + 128 + virtualHosts.${website-hostname}.extraConfig = 129 + # TODO: host website on server with automatic deployment 130 + '' 131 + reverse_proxy https://mrnossiom.github.io { 132 + header_up Host {http.request.host} 133 + } 134 + ''; 135 + 136 + virtualHosts.${static-hostname}.extraConfig = '' 137 + root /var/www/static 138 + file_server browse 139 + ''; 140 + }; 141 + 142 + # TODO: use bind to declare dns records declaratively 143 + }; 144 + }
+25
hosts/weird-row-server/goatcounter.nix
··· 1 + { config 2 + , ... 3 + }: 4 + 5 + let 6 + goatcounter-port = 3010; 7 + goatcounter-hostname = "stats.wiro.world"; 8 + in 9 + { 10 + config = { 11 + services.goatcounter = { 12 + enable = true; 13 + 14 + port = goatcounter-port; 15 + proxy = true; 16 + extraArgs = [ "-automigrate" ]; 17 + }; 18 + 19 + services.caddy = { 20 + virtualHosts.${goatcounter-hostname}.extraConfig = '' 21 + reverse_proxy http://localhost:${toString config.services.goatcounter.port} 22 + ''; 23 + }; 24 + }; 25 + }
+87
hosts/weird-row-server/grafana.nix
··· 1 + { config 2 + , ... 3 + }: 4 + 5 + let 6 + grafana-port = 3002; 7 + # grafana-hostname = "console.net.wiro.world"; 8 + grafana-hostname = "console.wiro.world"; 9 + 10 + prometheus-port = 9001; 11 + prometheus-node-exporter-port = 9002; 12 + caddy-metrics-port = 2019; 13 + authelia-metrics-port = 9004; 14 + headscale-metrics-port = 9003; 15 + in 16 + { 17 + config = { 18 + age.secrets.grafana-oidc-secret = { file = secrets/grafana-oidc-secret.age; owner = "grafana"; }; 19 + services.grafana = { 20 + enable = true; 21 + 22 + settings = { 23 + server = { 24 + http_port = grafana-port; 25 + domain = grafana-hostname; 26 + root_url = "https://${grafana-hostname}"; 27 + }; 28 + 29 + "auth.generic_oauth" = { 30 + enable = true; 31 + name = "Authelia"; 32 + icon = "signin"; 33 + 34 + client_id = "grafana"; 35 + client_secret_path = config.age.secrets.grafana-oidc-secret.path; 36 + auto_login = true; 37 + 38 + scopes = [ "openid" "profile" "email" "groups" ]; 39 + auth_url = "https://auth.wiro.world/api/oidc/authorization"; 40 + token_url = "https://auth.wiro.world/api/oidc/token"; 41 + api_url = "https://auth.wiro.world/api/oidc/userinfo"; 42 + use_pkce = true; 43 + }; 44 + }; 45 + }; 46 + 47 + services.prometheus = { 48 + enable = true; 49 + port = prometheus-port; 50 + 51 + exporters.node = { 52 + enable = true; 53 + port = prometheus-node-exporter-port; 54 + }; 55 + 56 + scrapeConfigs = [ 57 + { 58 + job_name = "caddy"; 59 + static_configs = [{ targets = [ "localhost:${toString caddy-metrics-port}" ]; }]; 60 + } 61 + { 62 + job_name = "node-exporter"; 63 + static_configs = [{ targets = [ "localhost:${toString config.services.prometheus.exporters.node.port}" ]; }]; 64 + } 65 + { 66 + job_name = "headscale"; 67 + static_configs = [{ targets = [ "localhost:${toString headscale-metrics-port}" ]; }]; 68 + } 69 + { 70 + job_name = "authelia"; 71 + static_configs = [{ targets = [ "localhost:${toString authelia-metrics-port}" ]; }]; 72 + } 73 + ]; 74 + }; 75 + 76 + services.caddy = { 77 + globalConfig = '' 78 + metrics { per_host } 79 + ''; 80 + # virtualHosts."http://${grafana-hostname}".extraConfig = '' 81 + # bind tailscale/console 82 + virtualHosts.${grafana-hostname}.extraConfig = '' 83 + reverse_proxy http://localhost:${toString grafana-port} 84 + ''; 85 + }; 86 + }; 87 + }
+84
hosts/weird-row-server/headscale.nix
··· 1 + { pkgs 2 + , config 3 + , ... 4 + }: 5 + 6 + let 7 + json-format = pkgs.formats.json { }; 8 + 9 + headscale-port = 3006; 10 + headscale-derp-port = 3478; 11 + headscale-hostname = "headscale.wiro.world"; 12 + 13 + headscale-metrics-port = 9003; 14 + in 15 + { 16 + config = { 17 + networking.firewall.allowedUDPPorts = [ headscale-derp-port ]; 18 + 19 + age.secrets.headscale-oidc-secret = { file = secrets/headscale-oidc-secret.age; owner = config.services.headscale.user; }; 20 + services.headscale = { 21 + enable = true; 22 + 23 + port = headscale-port; 24 + settings = { 25 + server_url = "https://${headscale-hostname}"; 26 + metrics_listen_addr = "127.0.0.1:${toString headscale-metrics-port}"; 27 + 28 + policy.path = json-format.generate "policy.json" { 29 + acls = [ 30 + { 31 + action = "accept"; 32 + src = [ "autogroup:member" ]; 33 + dst = [ "autogroup:self:*" ]; 34 + } 35 + ]; 36 + ssh = [ 37 + { 38 + action = "accept"; 39 + src = [ "autogroup:member" ]; 40 + dst = [ "autogroup:self" ]; 41 + # Adding root here is privilege escalation as a feature :) 42 + users = [ "autogroup:nonroot" ]; 43 + } 44 + ]; 45 + }; 46 + 47 + # disable TLS 48 + tls_cert_path = null; 49 + tls_key_path = null; 50 + 51 + dns = { 52 + magic_dns = true; 53 + base_domain = "net.wiro.world"; 54 + 55 + override_local_dns = true; 56 + # Quad9 nameservers 57 + nameservers.global = [ "9.9.9.9" "149.112.112.112" "2620:fe::fe" "2620:fe::9" ]; 58 + }; 59 + 60 + oidc = { 61 + only_start_if_oidc_is_available = true; 62 + issuer = "https://auth.wiro.world"; 63 + client_id = "headscale"; 64 + client_secret_path = config.age.secrets.headscale-oidc-secret.path; 65 + scope = [ "openid" "profile" "email" "groups" ]; 66 + pkce.enabled = true; 67 + }; 68 + 69 + derp.server = { 70 + enable = true; 71 + stun_listen_addr = "0.0.0.0:${toString headscale-derp-port}"; 72 + }; 73 + }; 74 + }; 75 + # headscale only starts if oidc is available 76 + systemd.services.headscale.after = [ "authelia-main.service" ]; 77 + 78 + services.caddy = { 79 + virtualHosts.${headscale-hostname}.extraConfig = '' 80 + reverse_proxy http://localhost:${toString headscale-port} 81 + ''; 82 + }; 83 + }; 84 + }
+42
hosts/weird-row-server/hypixel-bank-tracker.nix
··· 1 + { self 2 + , config 3 + , ... 4 + }: 5 + 6 + let 7 + inherit (self.inputs) hypixel-bank-tracker; 8 + 9 + hbt-main-port = 3013; 10 + hbt-banana-port = 3014; 11 + in 12 + { 13 + imports = [ hypixel-bank-tracker.nixosModules.default ]; 14 + 15 + config = { 16 + age.secrets.hypixel-bank-tracker-main.file = secrets/hypixel-bank-tracker-main.age; 17 + services.hypixel-bank-tracker.instances.main = { 18 + enable = true; 19 + 20 + port = hbt-main-port; 21 + environmentFile = config.age.secrets.hypixel-bank-tracker-main.path; 22 + }; 23 + 24 + age.secrets.hypixel-bank-tracker-banana.file = secrets/hypixel-bank-tracker-banana.age; 25 + services.hypixel-bank-tracker.instances.banana = { 26 + enable = true; 27 + 28 + port = hbt-banana-port; 29 + environmentFile = config.age.secrets.hypixel-bank-tracker-banana.path; 30 + }; 31 + 32 + services.caddy = { 33 + virtualHosts."hypixel-bank-tracker.xyz".extraConfig = '' 34 + reverse_proxy http://localhost:${toString config.services.hypixel-bank-tracker.instances.main.port} 35 + ''; 36 + 37 + virtualHosts."banana.hypixel-bank-tracker.xyz".extraConfig = '' 38 + reverse_proxy http://localhost:${toString config.services.hypixel-bank-tracker.instances.banana.port} 39 + ''; 40 + }; 41 + }; 42 + }
+39
hosts/weird-row-server/lldap.nix
··· 1 + { config 2 + , ... 3 + }: 4 + 5 + let 6 + lldap-port = 3007; 7 + lldap-hostname = "ldap.net.wiro.world"; 8 + in 9 + { 10 + config = { 11 + age.secrets.lldap-env.file = secrets/lldap-env.age; 12 + users.users.lldap = { isSystemUser = true; group = "lldap"; }; 13 + users.groups.lldap = { }; 14 + age.secrets.lldap-user-pass = { file = secrets/lldap-user-pass.age; owner = "lldap"; }; 15 + services.lldap = { 16 + enable = true; 17 + 18 + silenceForceUserPassResetWarning = true; 19 + 20 + settings = { 21 + http_url = "https://${lldap-hostname}"; 22 + http_port = lldap-port; 23 + 24 + ldap_user_pass_file = config.age.secrets.lldap-user-pass.path; 25 + force_ldap_user_pass_reset = false; 26 + 27 + ldap_base_dn = "dc=wiro,dc=world"; 28 + }; 29 + environmentFile = config.age.secrets.lldap-env.path; 30 + }; 31 + 32 + services.caddy = { 33 + virtualHosts."http://${lldap-hostname}".extraConfig = '' 34 + bind tailscale/ldap 35 + reverse_proxy http://localhost:${toString config.services.lldap.settings.http_port} 36 + ''; 37 + }; 38 + }; 39 + }
+52
hosts/weird-row-server/miniflux.nix
··· 1 + { config 2 + , ... 3 + }: 4 + 5 + let 6 + miniflux-port = 3012; 7 + miniflux-hostname = "news.wiro.world"; 8 + in 9 + { 10 + config = { 11 + users.users.miniflux = { isSystemUser = true; group = "miniflux"; }; 12 + users.groups.miniflux = { }; 13 + age.secrets.miniflux-oidc-secret = { file = secrets/miniflux-oidc-secret.age; owner = "miniflux"; }; 14 + services.miniflux = { 15 + enable = true; 16 + 17 + createDatabaseLocally = true; 18 + config = { 19 + BASE_URL = "https://${miniflux-hostname}/"; 20 + LISTEN_ADDR = "127.0.0.1:${toString miniflux-port}"; 21 + CREATE_ADMIN = 0; 22 + 23 + METRICS_COLLECTOR = 1; 24 + 25 + OAUTH2_PROVIDER = "oidc"; 26 + OAUTH2_OIDC_PROVIDER_NAME = "wiro.world SSO"; 27 + OAUTH2_CLIENT_ID = "miniflux"; 28 + OAUTH2_CLIENT_SECRET_FILE = config.age.secrets.miniflux-oidc-secret.path; 29 + OAUTH2_REDIRECT_URL = "https://${miniflux-hostname}/oauth2/oidc/callback"; 30 + OAUTH2_OIDC_DISCOVERY_ENDPOINT = "https://auth.wiro.world"; 31 + OAUTH2_USER_CREATION = 1; 32 + DISABLE_LOCAL_AUTH = 1; 33 + 34 + RUN_MIGRATIONS = 1; 35 + 36 + # NetNewsWire is a very good iOS oss client that integrates well 37 + # https://b.j4.lc/2025/05/05/setting-up-netnewswire-with-miniflux/ 38 + }; 39 + }; 40 + 41 + services.prometheus.scrapeConfigs = [{ 42 + job_name = "miniflux"; 43 + static_configs = [{ targets = [ "localhost:${toString miniflux-port}" ]; }]; 44 + }]; 45 + 46 + services.caddy = { 47 + virtualHosts.${miniflux-hostname}.extraConfig = '' 48 + reverse_proxy http://localhost:${toString miniflux-port} 49 + ''; 50 + }; 51 + }; 52 + }
+28
hosts/weird-row-server/paisa.nix
··· 1 + { config 2 + , pkgs 3 + , ... 4 + }: 5 + 6 + let 7 + # paisa-port = 3016; 8 + paisa-port = 7500; 9 + in 10 + { 11 + config = { 12 + services.paisa = { 13 + enable = true; 14 + port = paisa-port; 15 + host = "127.0.0.1"; 16 + 17 + settings = { }; 18 + }; 19 + systemd.services.paisa.path = [ pkgs.hledger ]; 20 + 21 + services.caddy = { 22 + virtualHosts."http://paisa.net.wiro.world".extraConfig = '' 23 + bind tailscale/paisa 24 + reverse_proxy http://localhost:${toString config.services.paisa.port} 25 + ''; 26 + }; 27 + }; 28 + }
+43
hosts/weird-row-server/pds.nix
··· 1 + { config 2 + , ... 3 + }: 4 + 5 + let 6 + pds-port = 3001; 7 + pds-hostname = "pds.wiro.world"; 8 + in 9 + { 10 + config = { 11 + age.secrets.pds-env.file = secrets/pds-env.age; 12 + services.bluesky-pds = { 13 + enable = true; 14 + 15 + settings = { 16 + PDS_HOSTNAME = "pds.wiro.world"; 17 + PDS_PORT = pds-port; 18 + # is in systemd /tmp subfolder 19 + LOG_DESTINATION = "/tmp/pds.log"; 20 + }; 21 + 22 + environmentFiles = [ 23 + config.age.secrets.pds-env.path 24 + ]; 25 + }; 26 + 27 + services.caddy = { 28 + globalConfig = '' 29 + on_demand_tls { 30 + ask http://localhost:${toString pds-port}/tls-check 31 + } 32 + ''; 33 + 34 + virtualHosts.${pds-hostname} = { 35 + serverAliases = [ "*.${pds-hostname}" ]; 36 + extraConfig = '' 37 + tls { on_demand } 38 + reverse_proxy http://localhost:${toString config.services.bluesky-pds.settings.PDS_PORT} 39 + ''; 40 + }; 41 + }; 42 + }; 43 + }
hosts/weird-row-server/secrets/authelia-issuer-private-key.age

This is a binary file and will not be displayed.

hosts/weird-row-server/secrets/authelia-jwt-secret.age

This is a binary file and will not be displayed.

hosts/weird-row-server/secrets/authelia-ldap-password.age

This is a binary file and will not be displayed.

+9
hosts/weird-row-server/secrets/authelia-smtp-password.age
··· 1 + age-encryption.org/v1 2 + -> ssh-ed25519 sMF1bg 0duJYaUlZd2G3tYi7CuV+c7KNaqWusLoMpoILvaajgc 3 + AnAlDVzRdeoXGXvyllhSNCoDQjZ+nucLGHCfPWR8IBQ 4 + -> ssh-ed25519 SmMcWg oX82fe4sQmKyjJqv9AFRY6Bww43V/8myNRsN9/M8Sho 5 + Et6ycO8hm2XV36X5q7iO+nJCtjkYoq6mDBEssrjt/70 6 + -> ssh-ed25519 Q8rMFA /gU1tIVjqExV8NXB1gSDsWIXpVfxm3zPJX7xOAqeqWM 7 + szrd67kHWslLO+jMCuDmzYR0LPVVzd7idgl3AKt+USU 8 + --- ojxyQVhx4rX+x5gzEEx8KFiorwywqEEyTNWUJY39jIk 9 + �������ş* l��1����= n�Ŏ��(� �Iamz~B����������� 鰦�.�.Z>�',p��
+11
hosts/weird-row-server/secrets/authelia-storage-key.age
··· 1 + age-encryption.org/v1 2 + -> ssh-ed25519 sMF1bg PYC6LaFo/DTQlRMewqbVtS4lzVtKyuFZQSqCrQwwEyg 3 + kj0sHy8IpnBsMBQy/XYtj2pLXlk6wprF3MxjFTOiZ6E 4 + -> ssh-ed25519 SmMcWg rvlLGzf6D96Dh1lL8as2vjc3PB7G/86U8AR7/lAELT0 5 + qcdOqVV6o+5OfJYj5pfZ62hOnrBQwY0DAeIJkLoyYss 6 + -> ssh-ed25519 Q8rMFA eALgQVN5OA0EuYYEyEbFnju/Yii71e+xcs98LkXnyyo 7 + Rbxu60qvXKdFaJ7lfs7LeYx37TIKYK2Gxw2QcxF3soA 8 + --- ryt5q5lXn1Cyn4T6RSU1IR7dHZOTbyanaG6ZqeYYGBQ 9 + �G���* \�ʕ�|��� 10 + �S�T���z�! �FIG͍��,��2Q��+�s�76Z�D`��Q�hM�],��E7�2<Q� 11 + ?ׯ�t��u\���xpe/rrR��L}��S��g�%�:�Oj����{T%h��D�~�+DЫ��W%� �ŗ�r�v$%m$
hosts/weird-row-server/secrets/caddy-env.age

This is a binary file and will not be displayed.

+27
hosts/weird-row-server/secrets/default.nix
··· 1 + keys: 2 + let 3 + inherit (keys) servers users; 4 + deploy = servers ++ users; 5 + in 6 + { 7 + # Defines `PDS_JWT_SECRET`, `PDS_ADMIN_PASSWORD`, `PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX`, `PDS_EMAIL_SMTP_URL`, `PDS_EMAIL_FROM_ADDRESS`. 8 + "pds-env.age".publicKeys = deploy; 9 + # Defines `LLDAP_JWT_SECRET`, `LLDAP_KEY_SEED`. 10 + "lldap-env.age".publicKeys = deploy; 11 + "lldap-user-pass.age".publicKeys = deploy; 12 + "headscale-oidc-secret.age".publicKeys = deploy; 13 + "grafana-oidc-secret.age".publicKeys = deploy; 14 + "authelia-jwt-secret.age".publicKeys = deploy; 15 + "authelia-issuer-private-key.age".publicKeys = deploy; 16 + "authelia-storage-key.age".publicKeys = deploy; 17 + "authelia-ldap-password.age".publicKeys = deploy; 18 + "authelia-smtp-password.age".publicKeys = deploy; 19 + "tuwunel-registration-tokens.age".publicKeys = deploy; 20 + # Defines `SMTP_PASSWORD` 21 + "vaultwarden-env.age".publicKeys = deploy; 22 + "miniflux-oidc-secret.age".publicKeys = deploy; 23 + # Defines `HYPIXEL_API_KEY`, `PROFILE_UUID` 24 + "hypixel-bank-tracker-main.age".publicKeys = deploy; 25 + "hypixel-bank-tracker-banana.age".publicKeys = deploy; 26 + "caddy-env.age".publicKeys = deploy; 27 + }
+9
hosts/weird-row-server/secrets/grafana-oidc-secret.age
··· 1 + age-encryption.org/v1 2 + -> ssh-ed25519 sMF1bg 2+JZ3WMnTgnnNYmJqdqAtOsEIk9pAefHfwLcXZQpqlg 3 + yE0R9b0mSzoT6mvoWUG829UmkgGkm6vg0i7WLORaTis 4 + -> ssh-ed25519 SmMcWg c8goAmCIPQ5rsTwoOej9ndGc0uBpWILSn/wy6XJHK14 5 + ArjlEl4hxi4N6Py1emxgxUFKvSXIwLWsy6HrWNqJUHw 6 + -> ssh-ed25519 Q8rMFA t72V+A0n5Tnb0VqvBrzt3j75CbcfvQltodKMhF64SXY 7 + V+Ynzd4tIUI8JPy9uyoi+frW6wJnQxDavHAqnp/lem8 8 + --- mzRky/dA1MDArnDSSaGyZP9yAQgD2va4MfopbrdM0iU 9 + ��*�٪l����a5�QE£ uަ��"�;��3�l.!` �u� _+)$��d ��ˤ����ff�F��mѳc�a�EV��Nߧ��,�@����
hosts/weird-row-server/secrets/headscale-oidc-secret.age

This is a binary file and will not be displayed.

hosts/weird-row-server/secrets/hypixel-bank-tracker-banana.age

This is a binary file and will not be displayed.

+12
hosts/weird-row-server/secrets/hypixel-bank-tracker-main.age
··· 1 + age-encryption.org/v1 2 + -> ssh-ed25519 sMF1bg B3wX5eWrSUivBKJzdiSOVvWEdGuZvGJHVDCCSFa29kw 3 + F1H0QzBPS1FPvactH7z3OvR5wPdawkMzIj1awUmkrqA 4 + -> ssh-ed25519 SmMcWg DtmTaO1Ox4y+AyZbOgaJU2PxMRdHloHECLOrvdI/g28 5 + my6GKU1rxPyClyos0efuInWok5w3xwwoJ4p3M6roBIw 6 + -> ssh-ed25519 Q8rMFA LKyAN4v78N/OcleCPuYpPlZ0se5Dmp4qiWynY4zTfDo 7 + 2lOqk3QF2lB/b7Md6yWq4R2jn+J3PiATIkrFUxgt104 8 + -> OR`n;K-grease 9 + lWGBER6K10zAcNHyiUMJAnJ84h1ppo5MJ5ztgw 10 + --- abe4RLNH3d9h7UpWD3PRIBgHWagt8cU+AHIFbX1D9/E 11 + �LJu���k؜��_��%��|�J]��KN��?]��tR�<�]�|���ߟ��¾FzU��������:�Y��TX����p,9v6q �7�~�y�]X���͇ǷR5՚>[������#�#�- 12 + cM,1Ǜ"M��"'F�W�3o�
hosts/weird-row-server/secrets/lldap-env.age

This is a binary file and will not be displayed.

+11
hosts/weird-row-server/secrets/lldap-user-pass.age
··· 1 + age-encryption.org/v1 2 + -> ssh-ed25519 sMF1bg rkX6nxV4pATfwzkzJtI08/Z0bvi0wYwnLJsppUAsz20 3 + X+znUc5AKAunPqr1jdq10obvQBU+rqs/LmeQaZzI+F4 4 + -> ssh-ed25519 SmMcWg xaZtLP6GOocdiMf3hgoujWigGgi7KxDUMGuPYNqS8WU 5 + 00Y+8L4QssKFqRlGB2e0yiv9SyoqbxeZ/lWL6DCLlpo 6 + -> ssh-ed25519 Q8rMFA kHA5wVVhmpodSn3OcZuc78xttymjOoy9voa8GqIE+F8 7 + a7HpZf6sjmxr49QoV9tXrATheu8u20JmKCEIkmAZdhA 8 + -> U=6S`1*-grease \8na]onc 9 + PP5XQ1vbpadHeiMBOkmEccTIAg 10 + --- 1hkJN6ZLX8GJ8y0WuFcMEv66om+Phc0wYJFKCB8Amzg 11 + �M� ��}ޘ*;f��y��]����2ef���}�d��� ���J�\��8�h��1�u� ������SHN��yS�D>W��x�J���yh������
+9
hosts/weird-row-server/secrets/miniflux-oidc-secret.age
··· 1 + age-encryption.org/v1 2 + -> ssh-ed25519 sMF1bg E4UVPOuq5ZUSxGvIvr0Tod9PQRqDdqHu2Byv4fKi2io 3 + FcdCyfLmRCmK5rmoLQ/m1KOJe9Etu9N/GHCM5lWCIPE 4 + -> ssh-ed25519 SmMcWg wrKv3V6uSLnWQqIp65Rgi0qv7lQtyOXaxnahMo+s3EU 5 + mXsJ1CbS3pzstf3xaWWF150+aXxW2kY2J5kAZWqtl+A 6 + -> ssh-ed25519 Q8rMFA 91npFfTkw9Ur6aZp/pLzLUOIwwPJ9OA1peaZyTlROBU 7 + 12sib8HLjvgN06X6H0/AN4wMewQ8xup813DauZKQ+QY 8 + --- /AGwAMAsPvvuRH6PPNrizBCsJedclYzdj6Kq4V3mx0o 9 + zN��0�=�YP������rլ�!U�n;���n���/��}mCo�F��������ž!z���r������)u��o�3�>��>Z�f�񡤙1Ň ����
hosts/weird-row-server/secrets/pds-env.age

This is a binary file and will not be displayed.

+9
hosts/weird-row-server/secrets/tuwunel-registration-tokens.age
··· 1 + age-encryption.org/v1 2 + -> ssh-ed25519 sMF1bg TVYRDtTe5khTJo0q8ShrR5o1WBrbK2htHjYCvi5QYAA 3 + kx6Hke5RAZFfugR4aU28SRh4U8e4ymzeIY/+kYlAWhw 4 + -> ssh-ed25519 SmMcWg AyJOM5lQHETeGiI/V5vUtu2vD6PqCZNnuTPvfnU90zE 5 + 9vM7/8JUbScHaeDWig16MgqtULryofSrRqhw2OMWfBs 6 + -> ssh-ed25519 Q8rMFA TeUNtmHquyhhDrXf+zXY56oTGvzkhkaReIoBx5Yb+TE 7 + DLfVy9cO1JrVln9CHV1ag66z2kIMrVzhcaIugLytojE 8 + --- nr/3KZTVXNdemLdmp2bO2bjxKDHvcy3gezZKYN5Z1qI 9 + �"��Z8��ԛ�GvJ�{��(�V9��1�N"��o���o����Х�oJ�~�1�!�}Egd�!�
hosts/weird-row-server/secrets/vaultwarden-env.age

This is a binary file and will not be displayed.

+54
hosts/weird-row-server/tangled.nix
··· 1 + { self 2 + , config 3 + , ... 4 + }: 5 + 6 + let 7 + inherit (self.inputs) tangled; 8 + 9 + tangled-owner = "did:plc:xhgrjm4mcx3p5h3y6eino6ti"; 10 + tangled-knot-port = 3003; 11 + tangled-knot-hostname = "knot.wiro.world"; 12 + tangled-spindle-port = 3004; 13 + tangled-spindle-hostname = "spindle.wiro.world"; 14 + in 15 + { 16 + imports = [ 17 + tangled.nixosModules.knot 18 + tangled.nixosModules.spindle 19 + ]; 20 + 21 + config = { 22 + services.tangled.knot = { 23 + enable = true; 24 + openFirewall = true; 25 + 26 + motd = "Welcome to @wiro.world's knot!\n"; 27 + server = { 28 + listenAddr = "localhost:${toString tangled-knot-port}"; 29 + hostname = tangled-knot-hostname; 30 + owner = tangled-owner; 31 + }; 32 + }; 33 + 34 + services.tangled.spindle = { 35 + enable = true; 36 + 37 + server = { 38 + listenAddr = "localhost:${toString tangled-spindle-port}"; 39 + hostname = tangled-spindle-hostname; 40 + owner = tangled-owner; 41 + }; 42 + }; 43 + 44 + services.caddy = { 45 + virtualHosts.${tangled-knot-hostname}.extraConfig = '' 46 + reverse_proxy http://localhost:${toString tangled-knot-port} 47 + ''; 48 + 49 + virtualHosts.${tangled-spindle-hostname}.extraConfig = '' 50 + reverse_proxy http://localhost:${toString tangled-spindle-port} 51 + ''; 52 + }; 53 + }; 54 + }
+31
hosts/weird-row-server/thelounge.nix
··· 1 + { config 2 + , ... 3 + }: 4 + 5 + let 6 + thelounge-port = 3005; 7 + thelounge-hostname = "irc-lounge.net.wiro.world"; 8 + in 9 + { 10 + config = { 11 + services.thelounge = { 12 + enable = true; 13 + port = thelounge-port; 14 + public = false; 15 + 16 + extraConfig = { 17 + host = "127.0.0.1"; 18 + reverseProxy = true; 19 + 20 + # TODO: use ldap, find a way to hide password 21 + }; 22 + }; 23 + 24 + services.caddy = { 25 + virtualHosts."http://${thelounge-hostname}".extraConfig = '' 26 + bind tailscale/irc-lounge 27 + reverse_proxy http://localhost:${toString config.services.thelounge.port} 28 + ''; 29 + }; 30 + }; 31 + }
+45
hosts/weird-row-server/tuwunel.nix
··· 1 + { config 2 + , ... 3 + }: 4 + 5 + let 6 + matrix-port = 3009; 7 + matrix-hostname = "matrix.wiro.world"; 8 + 9 + website-hostname = "wiro.world"; 10 + in 11 + { 12 + config = { 13 + age.secrets.tuwunel-registration-tokens = { file = secrets/tuwunel-registration-tokens.age; owner = config.services.matrix-tuwunel.user; }; 14 + services.matrix-tuwunel = { 15 + enable = true; 16 + 17 + settings.global = { 18 + address = [ "127.0.0.1" ]; 19 + port = [ matrix-port ]; 20 + 21 + server_name = "wiro.world"; 22 + well_known = { 23 + client = "https://matrix.wiro.world"; 24 + server = "matrix.wiro.world:443"; 25 + }; 26 + 27 + grant_admin_to_first_user = true; 28 + new_user_displayname_suffix = ""; 29 + 30 + allow_registration = true; 31 + registration_token_file = config.age.secrets.tuwunel-registration-tokens.path; 32 + }; 33 + }; 34 + 35 + services.caddy = { 36 + virtualHosts.${matrix-hostname}.extraConfig = '' 37 + reverse_proxy /_matrix/* http://localhost:${toString matrix-port} 38 + ''; 39 + 40 + virtualHosts.${website-hostname}.extraConfig = '' 41 + reverse_proxy /.well-known/matrix/* http://localhost:${toString matrix-port} 42 + ''; 43 + }; 44 + }; 45 + }
+38
hosts/weird-row-server/vaultwarden.nix
··· 1 + { config 2 + , ... 3 + }: 4 + 5 + let 6 + vaultwarden-port = 3011; 7 + vaultwarden-hostname = "vault.wiro.world"; 8 + in 9 + { 10 + config = { 11 + age.secrets.vaultwarden-env.file = secrets/vaultwarden-env.age; 12 + services.vaultwarden = { 13 + enable = true; 14 + 15 + environmentFile = config.age.secrets.vaultwarden-env.path; 16 + config = { 17 + ROCKET_PORT = vaultwarden-port; 18 + DOMAIN = "https://${vaultwarden-hostname}"; 19 + SIGNUPS_ALLOWED = false; 20 + ADMIN_TOKEN = "$argon2id$v=19$m=65540,t=3,p=4$YIe9wmrTsmjgZNPxe8m34O/d3XW3Fl/uZPPLQs79dAc$mjDVQSdBJqz2uBJuxtAvCIoHPzOnTDhNPuhER3dhHrY"; 21 + 22 + SMTP_HOST = "smtp.resend.com"; 23 + SMTP_PORT = 2465; 24 + SMTP_SECURITY = "force_tls"; 25 + SMTP_USERNAME = "resend"; 26 + # SMTP_PASSWORD = ...; # Via secret env 27 + SMTP_FROM = "bitwarden@wiro.world"; 28 + SMTP_FROM_NAME = "Bitwarden wiro.world"; 29 + }; 30 + }; 31 + 32 + services.caddy = { 33 + virtualHosts.${vaultwarden-hostname}.extraConfig = '' 34 + reverse_proxy http://localhost:${toString config.services.vaultwarden.config.ROCKET_PORT} 35 + ''; 36 + }; 37 + }; 38 + }
+29
hosts/weird-row-server/warrior.nix
··· 1 + { ... 2 + }: 3 + 4 + let 5 + warrior-port = 3015; 6 + warrior-hostname = "warrior.net.wiro.world"; 7 + in 8 + { 9 + config = { 10 + virtualisation.oci-containers.containers.archive-warrior = { 11 + image = "atdr.meo.ws/archiveteam/warrior-dockerfile"; 12 + ports = [ "127.0.0.1:${toString warrior-port}:8001" ]; 13 + pull = "newer"; 14 + 15 + environment = { 16 + DOWNLOADER = "wiro"; 17 + SELECTED_PROJECT = "urls"; 18 + CONCURRENT_ITEMS = "6"; 19 + }; 20 + }; 21 + 22 + services.caddy = { 23 + virtualHosts."http://${warrior-hostname}".extraConfig = '' 24 + bind tailscale/warrior 25 + reverse_proxy http://localhost:${toString warrior-port} 26 + ''; 27 + }; 28 + }; 29 + }
+77
hosts/weird-row-server/webfinger.nix
··· 1 + { pkgs 2 + , config 3 + , ... 4 + }: 5 + 6 + let 7 + webfinger-dir = pkgs.writeTextDir ".well-known/webfinger" '' 8 + { 9 + "subject": "acct:milo@wiro.world", 10 + "aliases": [ 11 + "mailto:milo@wiro.world", 12 + "https://wiro.world/" 13 + ], 14 + "links": [ 15 + { 16 + "rel": "http://wiro.world/rel/avatar", 17 + "href": "https://wiro.world/logo.jpg", 18 + "type": "image/jpeg" 19 + }, 20 + { 21 + "rel": "http://webfinger.net/rel/profile-page", 22 + "href": "https://wiro.world/", 23 + "type": "text/html" 24 + }, 25 + { 26 + "rel": "http://openid.net/specs/connect/1.0/issuer", 27 + "href": "https://auth.wiro.world" 28 + } 29 + ] 30 + } 31 + ''; 32 + 33 + well-known-discord-dir = pkgs.writeTextDir ".well-known/discord" '' 34 + dh=919234284ceb2aba439d15b9136073eb2308989b 35 + ''; 36 + 37 + website-hostname = "wiro.world"; 38 + in 39 + { 40 + config = { 41 + services.caddy = { 42 + virtualHosts.${website-hostname}.extraConfig = '' 43 + @webfinger { 44 + path /.well-known/webfinger 45 + method GET HEAD 46 + query resource=acct:milo@wiro.world 47 + query resource=mailto:milo@wiro.world 48 + query resource=https://wiro.world 49 + query resource=https://wiro.world/ 50 + } 51 + route @webfinger { 52 + header { 53 + Content-Type "application/jrd+json" 54 + Access-Control-Allow-Origin "*" 55 + X-Robots-Tag "noindex" 56 + } 57 + root ${webfinger-dir} 58 + file_server 59 + } 60 + '' + 61 + '' 62 + @discord { 63 + path /.well-known/discord 64 + method GET HEAD 65 + } 66 + route @discord { 67 + header { 68 + Access-Control-Allow-Origin "*" 69 + X-Robots-Tag "noindex" 70 + } 71 + root ${well-known-discord-dir} 72 + file_server 73 + } 74 + ''; 75 + }; 76 + }; 77 + }
+12 -2
lib/flake/default.nix
··· 20 20 # local packages set 21 21 lpkgs = import ../../pkgs pkgs; 22 22 # unstable nixpkgs set 23 - upkgs = import unixpkgs { inherit (pkgs) system config; }; 23 + upkgs = import unixpkgs { 24 + config = pkgs.config; 25 + system = pkgs.stdenv.hostPlatform.system; 26 + }; 24 27 # indicates if system is darwin 25 28 isDarwin = pkgs.stdenv.isDarwin; 26 29 }; ··· 41 44 ../../nixos/hardware/${hostName}.nix 42 45 ../../nixos/profiles/${profile}.nix 43 46 ]; 44 - networking.hostName = hostName; 47 + config.networking.hostName = hostName; 48 + }; 49 + host = hostName: { 50 + imports = [ 51 + ../../nixos/hardware/${hostName}.nix 52 + ../../hosts/${hostName}/default.nix 53 + ]; 54 + config.networking.hostName = hostName; 45 55 }; 46 56 user = import ./user.nix; 47 57 managedDiskLayout = import ./managedDiskLayout.nix;
+1 -1
lib/flake/managedDiskLayout.nix
··· 26 26 27 27 The recommended amount from RedHat is: 28 28 29 - Amount of RAM Recommended swap space Recommended swap space 29 + Amount of RAM Recommended swap space Recommended swap space 30 30 in the system if allowing for hibernation 31 31 —————————————— —————————————————————————— ——————————————————————————— 32 32 ⩽ 2 GB 2 times the amount of RAM 3 times the amount of RAM
+1
lib/flake/user.nix
··· 36 36 } else { 37 37 home = "/home/${name}"; 38 38 extraGroups = [ 39 + # TODO: remove or put under an condition 39 40 "wheel" # sudo access 40 41 "networkmanager" # needed for nm 41 42 ];
+2 -3
modules/home-manager/default.nix
··· 1 1 { 2 - wakatime = import ./wakatime.nix; 3 - wl-clip-persist = import ./wl-clip-persist.nix; 4 - xcompose = import ./xcompose.nix; 2 + wakatime = ./wakatime.nix; 3 + xcompose = ./xcompose.nix; 5 4 }
-99
modules/home-manager/wl-clip-persist.nix
··· 1 - { config 2 - , lib 3 - , pkgs 4 - , ... 5 - }: 6 - 7 - let 8 - cfg = config.services.wl-clip-persist; 9 - in 10 - { 11 - options.services.wl-clip-persist = with lib; { 12 - enable = mkEnableOption ""; 13 - 14 - package = mkPackageOption pkgs "wl-clip-persist" { }; 15 - 16 - clipboard = mkOption { 17 - description = "The clipboard type to operate on"; 18 - default = "regular"; 19 - type = types.enum [ "regular" "primary" "both" ]; 20 - }; 21 - 22 - display = mkOption { 23 - description = "The wayland display to operate on"; 24 - default = null; 25 - type = types.nullOr types.str; 26 - }; 27 - 28 - ignoreEventOnError = mkOption { 29 - description = "Only handle selection events where no error occurred"; 30 - default = null; 31 - type = types.nullOr types.bool; 32 - }; 33 - 34 - allMimeTypeRegex = mkOption { 35 - description = "Only handle selection events where all offered MIME types have a match for the regex"; 36 - default = null; 37 - type = types.nullOr types.str; 38 - }; 39 - 40 - interruptOldClipboardRequests = mkOption { 41 - description = "Interrupt trying to send the old clipboard to other programs when the clipboard has been updated"; 42 - default = null; 43 - type = types.nullOr types.bool; 44 - }; 45 - 46 - selectionSizeLimit = mkOption { 47 - description = "Only handle selection events whose total data size does not exceed the size limit"; 48 - default = null; 49 - type = types.nullOr types.int; 50 - }; 51 - 52 - readTimeout = mkOption { 53 - description = "Timeout for trying to get the current clipboard"; 54 - default = 500; 55 - type = types.int; 56 - }; 57 - 58 - ignoreEventOnTimeout = mkOption { 59 - description = "Only handle selection events where no timeout occurred"; 60 - default = null; 61 - type = types.nullOr types.bool; 62 - }; 63 - 64 - writeTimeout = mkOption { 65 - description = "Timeout for trying to send the current clipboard to other programs"; 66 - default = 3000; 67 - type = types.int; 68 - }; 69 - }; 70 - 71 - config = lib.mkIf cfg.enable { 72 - systemd.user.services.wl-clip-persist = { 73 - Unit = { 74 - Description = "wl-clip-persist system service"; 75 - PartOf = [ "graphical-session.target" ]; 76 - BindsTo = [ "graphical-session.target" ]; 77 - }; 78 - 79 - Service = { 80 - Type = "simple"; 81 - ExecStart = "${lib.getExe cfg.package} ${lib.cli.toGNUCommandLineShell {} { 82 - clipboard = cfg.clipboard; 83 - display = cfg.display; 84 - ignore-event-on-error = cfg.ignoreEventOnError; 85 - all-mime-type-regex = cfg.allMimeTypeRegex; 86 - interrupt-old-clipboard-requests = cfg.interruptOldClipboardRequests; 87 - selection-size-limit = cfg.selectionSizeLimit; 88 - read-timeout = cfg.readTimeout; 89 - ignore-event-on-timeout = cfg.ignoreEventOnTimeout; 90 - write-timeout = cfg.writeTimeout; 91 - }}"; 92 - Restart = "on-failure"; 93 - TimeoutStopSec = 15; 94 - }; 95 - 96 - Install.WantedBy = lib.mkDefault [ "graphical-session.target" ]; 97 - }; 98 - }; 99 - }
+1 -1
modules/home-manager/xcompose.nix
··· 14 14 loadConfigInEnv = mkOption { 15 15 description = '' 16 16 Load the XCompose file by passing the `XCOMPOSEFILE` environment variable instead of linking to ~/.XCompose. 17 - 17 + 18 18 That is nice to avoid cluttering the HOME directory, it's preferable to disable it when experimenting 19 19 with your compose config to reload faster than having to reload your VM. 20 20 '';
-2
modules/nixos/default.nix
··· 1 1 { 2 - geoclue2 = ./geoclue2.nix; 3 - headscale = ./headscale.nix; 4 2 logiops = ./logiops.nix; 5 3 }
-324
modules/nixos/geoclue2.nix
··· 1 - # Adapted from the original nixpkgs repo 2 - # 3 - # It supports static location fallback. This is a workaround waiting for an 4 - # alternative to MLS (Mozilla Location Services). 5 - 6 - # Target interface to manage the static file would be: 7 - # static = { 8 - # latitude = 48.8; 9 - # longitude = 2.3; 10 - # }; 11 - # 12 - # I spent way too much time getting this to work with a submodule. 13 - 14 - { config 15 - , lib 16 - , pkgs 17 - , ... 18 - }: 19 - 20 - let 21 - package = pkgs.geoclue2.override { withDemoAgent = config.services.geoclue2.enableDemoAgent; }; 22 - 23 - cfg = config.services.geoclue2; 24 - 25 - defaultWhitelist = [ "gnome-shell" "io.elementary.desktop.agent-geoclue2" ]; 26 - 27 - appConfigModule = lib.types.submodule ({ name, ... }: with lib; { 28 - options = { 29 - desktopID = mkOption { 30 - type = types.str; 31 - description = "Desktop ID of the application."; 32 - }; 33 - 34 - isAllowed = mkOption { 35 - type = types.bool; 36 - description = '' 37 - Whether the application will be allowed access to location information. 38 - ''; 39 - }; 40 - 41 - isSystem = mkOption { 42 - type = types.bool; 43 - description = '' 44 - Whether the application is a system component or not. 45 - ''; 46 - }; 47 - 48 - users = mkOption { 49 - type = types.listOf types.str; 50 - default = [ ]; 51 - description = '' 52 - List of UIDs of all users for which this application is allowed location 53 - info access, Defaults to an empty string to allow it for all users. 54 - ''; 55 - }; 56 - }; 57 - 58 - config.desktopID = mkDefault name; 59 - }); 60 - 61 - # staticModule = types.submodule ({ name, ... }: { 62 - # options = { 63 - # latitude = mkOption { 64 - # type = types.float; 65 - # example = 40.6893129; 66 - # }; 67 - 68 - # longitude = mkOption { 69 - # type = types.float; 70 - # example = -74.0445531; 71 - # }; 72 - 73 - # altitude = mkOption { 74 - # type = types.float; 75 - # default = 0; 76 - # example = 96; 77 - # }; 78 - 79 - # accuracyRadius = mkOption { 80 - # type = types.float; 81 - # default = 0; 82 - # example = 1.83; 83 - # }; 84 - # }; 85 - # }); 86 - 87 - appConfigToINICompatible = _: { desktopID, isAllowed, isSystem, users, ... }: { 88 - name = desktopID; 89 - value = { 90 - allowed = isAllowed; 91 - system = isSystem; 92 - users = lib.concatStringsSep ";" users; 93 - }; 94 - }; 95 - 96 - in 97 - { 98 - disabledModules = [ "services/desktops/geoclue2.nix" ]; 99 - 100 - options.services.geoclue2 = with lib; { 101 - enable = mkOption { 102 - type = types.bool; 103 - default = false; 104 - description = '' 105 - Whether to enable GeoClue 2 daemon, a DBus service 106 - that provides location information for accessing. 107 - ''; 108 - }; 109 - 110 - enableDemoAgent = mkOption { 111 - type = types.bool; 112 - default = true; 113 - description = '' 114 - Whether to use the GeoClue demo agent. This should be 115 - overridden by desktop environments that provide their own 116 - agent. 117 - ''; 118 - }; 119 - 120 - enableNmea = mkOption { 121 - type = types.bool; 122 - default = true; 123 - description = '' 124 - Whether to fetch location from NMEA sources on local network. 125 - ''; 126 - }; 127 - 128 - enable3G = mkOption { 129 - type = types.bool; 130 - default = true; 131 - description = '' 132 - Whether to enable 3G source. 133 - ''; 134 - }; 135 - 136 - enableCDMA = mkOption { 137 - type = types.bool; 138 - default = true; 139 - description = '' 140 - Whether to enable CDMA source. 141 - ''; 142 - }; 143 - 144 - enableModemGPS = mkOption { 145 - type = types.bool; 146 - default = true; 147 - description = '' 148 - Whether to enable Modem-GPS source. 149 - ''; 150 - }; 151 - 152 - enableWifi = mkOption { 153 - type = types.bool; 154 - default = true; 155 - description = '' 156 - Whether to enable WiFi source. 157 - ''; 158 - }; 159 - 160 - geoProviderUrl = mkOption { 161 - type = types.str; 162 - default = "https://location.services.mozilla.com/v1/geolocate?key=geoclue"; 163 - example = "https://www.googleapis.com/geolocation/v1/geolocate?key=YOUR_KEY"; 164 - description = '' 165 - The url to the wifi GeoLocation Service. 166 - ''; 167 - }; 168 - 169 - submitData = mkOption { 170 - type = types.bool; 171 - default = false; 172 - description = '' 173 - Whether to submit data to a GeoLocation Service. 174 - ''; 175 - }; 176 - 177 - submissionUrl = mkOption { 178 - type = types.str; 179 - default = "https://location.services.mozilla.com/v1/submit?key=geoclue"; 180 - description = '' 181 - The url to submit data to a GeoLocation Service. 182 - ''; 183 - }; 184 - 185 - submissionNick = mkOption { 186 - type = types.str; 187 - default = "geoclue"; 188 - description = '' 189 - A nickname to submit network data with. 190 - Must be 2-32 characters long. 191 - ''; 192 - }; 193 - 194 - appConfig = mkOption { 195 - type = types.attrsOf appConfigModule; 196 - default = { }; 197 - example = { 198 - "com.github.app" = { 199 - isAllowed = true; 200 - isSystem = true; 201 - users = [ "300" ]; 202 - }; 203 - }; 204 - description = '' 205 - Specify extra settings per application. 206 - ''; 207 - }; 208 - 209 - # static = mkOption { 210 - # type = types.nullOr (types.attrsOf staticModule); 211 - # default = null; 212 - # description = '' 213 - # Add a fallback location that will be overridden by other location services 214 - # ''; 215 - # }; 216 - 217 - staticFile = mkOption { 218 - type = types.nullOr (types.str); 219 - default = null; 220 - description = '' 221 - Add a fallback location that will be overridden by other location services 222 - ''; 223 - }; 224 - }; 225 - 226 - config = lib.mkIf cfg.enable { 227 - environment.systemPackages = [ package ]; 228 - 229 - services.dbus.packages = [ package ]; 230 - 231 - systemd.packages = [ package ]; 232 - 233 - # we cannot use DynamicUser as we need the the geoclue user to exist for the 234 - # dbus policy to work 235 - users = { 236 - users.geoclue = { 237 - isSystemUser = true; 238 - home = "/var/lib/geoclue"; 239 - group = "geoclue"; 240 - description = "Geoinformation service"; 241 - }; 242 - 243 - groups.geoclue = { }; 244 - }; 245 - 246 - systemd.services.geoclue = { 247 - wants = lib.optionals cfg.enableWifi [ "network-online.target" ]; 248 - after = lib.optionals cfg.enableWifi [ "network-online.target" ]; 249 - # restart geoclue service when the configuration changes 250 - restartTriggers = [ 251 - config.environment.etc."geoclue/geoclue.conf".source 252 - ]; 253 - serviceConfig.StateDirectory = "geoclue"; 254 - }; 255 - 256 - # this needs to run as a user service, since it's associated with the 257 - # user who is making the requests 258 - systemd.user.services = lib.mkIf cfg.enableDemoAgent { 259 - geoclue-agent = { 260 - description = "Geoclue agent"; 261 - # this should really be `partOf = [ "geoclue.service" ]`, but 262 - # we can't be part of a system service, and the agent should 263 - # be okay with the main service coming and going 264 - wantedBy = [ "default.target" ]; 265 - wants = lib.optionals cfg.enableWifi [ "network-online.target" ]; 266 - after = lib.optionals cfg.enableWifi [ "network-online.target" ]; 267 - unitConfig.ConditionUser = "!@system"; 268 - serviceConfig = { 269 - Type = "exec"; 270 - ExecStart = "${package}/libexec/geoclue-2.0/demos/agent"; 271 - Restart = "on-failure"; 272 - PrivateTmp = true; 273 - }; 274 - }; 275 - }; 276 - 277 - services.geoclue2.appConfig = { 278 - epiphany = { isAllowed = true; isSystem = false; }; 279 - firefox = { isAllowed = true; isSystem = false; }; 280 - }; 281 - 282 - environment.etc."geoclue/geoclue.conf".text = 283 - lib.generators.toINI { } ({ 284 - agent = { 285 - whitelist = lib.concatStringsSep ";" 286 - (lib.optional cfg.enableDemoAgent "geoclue-demo-agent" ++ defaultWhitelist); 287 - }; 288 - network-nmea = { 289 - enable = cfg.enableNmea; 290 - }; 291 - "3g" = { 292 - enable = cfg.enable3G; 293 - }; 294 - cdma = { 295 - enable = cfg.enableCDMA; 296 - }; 297 - modem-gps = { 298 - enable = cfg.enableModemGPS; 299 - }; 300 - wifi = { 301 - enable = cfg.enableWifi; 302 - url = cfg.geoProviderUrl; 303 - submit-data = lib.boolToString cfg.submitData; 304 - submission-url = cfg.submissionUrl; 305 - submission-nick = cfg.submissionNick; 306 - }; 307 - } // lib.mapAttrs' appConfigToINICompatible cfg.appConfig); 308 - 309 - # environment.etc."geolocation" = mkIf (cfg.static != null) { 310 - # text = '' 311 - # ${toString cfg.static.latitude} 312 - # ${toString cfg.static.longitude} 313 - # ${toString cfg.static.altitude} 314 - # ${toString cfg.static.accuracyRadius} 315 - # ''; 316 - # }; 317 - 318 - environment.etc."geolocation" = lib.mkIf (cfg.staticFile != null) { text = cfg.staticFile; }; 319 - }; 320 - 321 - meta = with lib; { 322 - maintainers = with maintainers; [ ] ++ teams.pantheon.members; 323 - }; 324 - }
-673
modules/nixos/headscale.nix
··· 1 - { config 2 - , lib 3 - , pkgs 4 - , ... 5 - }: 6 - let 7 - cfg = config.services.headscale; 8 - 9 - dataDir = "/var/lib/headscale"; 10 - runDir = "/run/headscale"; 11 - 12 - cliConfig = { 13 - # Turn off update checks since the origin of our package 14 - # is nixpkgs and not Github. 15 - disable_check_updates = true; 16 - 17 - unix_socket = "${runDir}/headscale.sock"; 18 - }; 19 - 20 - settingsFormat = pkgs.formats.yaml { }; 21 - configFile = settingsFormat.generate "headscale.yaml" cfg.settings; 22 - cliConfigFile = settingsFormat.generate "headscale.yaml" cliConfig; 23 - 24 - assertRemovedOption = option: message: { 25 - assertion = !lib.hasAttrByPath option cfg; 26 - message = 27 - "The option `services.headscale.${lib.options.showOption option}` was removed. " + message; 28 - }; 29 - in 30 - { 31 - options = { 32 - services.headscale = { 33 - enable = lib.mkEnableOption "headscale, Open Source coordination server for Tailscale"; 34 - 35 - package = lib.mkPackageOption pkgs "headscale" { }; 36 - 37 - user = lib.mkOption { 38 - default = "headscale"; 39 - type = lib.types.str; 40 - description = '' 41 - User account under which headscale runs. 42 - 43 - ::: {.note} 44 - If left as the default value this user will automatically be created 45 - on system activation, otherwise you are responsible for 46 - ensuring the user exists before the headscale service starts. 47 - ::: 48 - ''; 49 - }; 50 - 51 - group = lib.mkOption { 52 - default = "headscale"; 53 - type = lib.types.str; 54 - description = '' 55 - Group under which headscale runs. 56 - 57 - ::: {.note} 58 - If left as the default value this group will automatically be created 59 - on system activation, otherwise you are responsible for 60 - ensuring the user exists before the headscale service starts. 61 - ::: 62 - ''; 63 - }; 64 - 65 - address = lib.mkOption { 66 - type = lib.types.str; 67 - default = "127.0.0.1"; 68 - description = '' 69 - Listening address of headscale. 70 - ''; 71 - example = "0.0.0.0"; 72 - }; 73 - 74 - port = lib.mkOption { 75 - type = lib.types.port; 76 - default = 8080; 77 - description = '' 78 - Listening port of headscale. 79 - ''; 80 - example = 443; 81 - }; 82 - 83 - settings = lib.mkOption { 84 - description = '' 85 - Overrides to {file}`config.yaml` as a Nix attribute set. 86 - Check the [example config](https://github.com/juanfont/headscale/blob/main/config-example.yaml) 87 - for possible options. 88 - ''; 89 - type = lib.types.submodule { 90 - freeformType = settingsFormat.type; 91 - 92 - options = { 93 - server_url = lib.mkOption { 94 - type = lib.types.str; 95 - default = "http://127.0.0.1:8080"; 96 - description = '' 97 - The url clients will connect to. 98 - ''; 99 - example = "https://myheadscale.example.com:443"; 100 - }; 101 - 102 - noise.private_key_path = lib.mkOption { 103 - type = lib.types.path; 104 - default = "${dataDir}/noise_private.key"; 105 - description = '' 106 - Path to noise private key file, generated automatically if it does not exist. 107 - ''; 108 - }; 109 - 110 - prefixes = 111 - let 112 - prefDesc = '' 113 - Each prefix consists of either an IPv4 or IPv6 address, 114 - and the associated prefix length, delimited by a slash. 115 - It must be within IP ranges supported by the Tailscale 116 - client - i.e., subnets of 100.64.0.0/10 and fd7a:115c:a1e0::/48. 117 - ''; 118 - in 119 - { 120 - v4 = lib.mkOption { 121 - type = lib.types.str; 122 - default = "100.64.0.0/10"; 123 - description = prefDesc; 124 - }; 125 - 126 - v6 = lib.mkOption { 127 - type = lib.types.str; 128 - default = "fd7a:115c:a1e0::/48"; 129 - description = prefDesc; 130 - }; 131 - 132 - allocation = lib.mkOption { 133 - type = lib.types.enum [ 134 - "sequential" 135 - "random" 136 - ]; 137 - example = "random"; 138 - default = "sequential"; 139 - description = '' 140 - Strategy used for allocation of IPs to nodes, available options: 141 - - sequential (default): assigns the next free IP from the previous given IP. 142 - - random: assigns the next free IP from a pseudo-random IP generator (crypto/rand). 143 - ''; 144 - }; 145 - }; 146 - 147 - derp = { 148 - urls = lib.mkOption { 149 - type = lib.types.listOf lib.types.str; 150 - default = [ "https://controlplane.tailscale.com/derpmap/default" ]; 151 - description = '' 152 - List of urls containing DERP maps. 153 - See [How Tailscale works](https://tailscale.com/blog/how-tailscale-works/) for more information on DERP maps. 154 - ''; 155 - }; 156 - 157 - paths = lib.mkOption { 158 - type = lib.types.listOf lib.types.path; 159 - default = [ ]; 160 - description = '' 161 - List of file paths containing DERP maps. 162 - See [How Tailscale works](https://tailscale.com/blog/how-tailscale-works/) for more information on DERP maps. 163 - ''; 164 - }; 165 - 166 - auto_update_enable = lib.mkOption { 167 - type = lib.types.bool; 168 - default = true; 169 - description = '' 170 - Whether to automatically update DERP maps on a set frequency. 171 - ''; 172 - example = false; 173 - }; 174 - 175 - update_frequency = lib.mkOption { 176 - type = lib.types.str; 177 - default = "24h"; 178 - description = '' 179 - Frequency to update DERP maps. 180 - ''; 181 - example = "5m"; 182 - }; 183 - 184 - server.private_key_path = lib.mkOption { 185 - type = lib.types.path; 186 - default = "${dataDir}/derp_server_private.key"; 187 - description = '' 188 - Path to derp private key file, generated automatically if it does not exist. 189 - ''; 190 - }; 191 - }; 192 - 193 - ephemeral_node_inactivity_timeout = lib.mkOption { 194 - type = lib.types.str; 195 - default = "30m"; 196 - description = '' 197 - Time before an inactive ephemeral node is deleted. 198 - ''; 199 - example = "5m"; 200 - }; 201 - 202 - database = { 203 - type = lib.mkOption { 204 - type = lib.types.enum [ 205 - "sqlite" 206 - "sqlite3" 207 - "postgres" 208 - ]; 209 - example = "postgres"; 210 - default = "sqlite"; 211 - description = '' 212 - Database engine to use. 213 - Please note that using Postgres is highly discouraged as it is only supported for legacy reasons. 214 - All new development, testing and optimisations are done with SQLite in mind. 215 - ''; 216 - }; 217 - 218 - sqlite = { 219 - path = lib.mkOption { 220 - type = lib.types.nullOr lib.types.str; 221 - default = "${dataDir}/db.sqlite"; 222 - description = "Path to the sqlite3 database file."; 223 - }; 224 - 225 - write_ahead_log = lib.mkOption { 226 - type = lib.types.bool; 227 - default = true; 228 - description = '' 229 - Enable WAL mode for SQLite. This is recommended for production environments. 230 - <https://www.sqlite.org/wal.html> 231 - ''; 232 - example = true; 233 - }; 234 - }; 235 - 236 - postgres = { 237 - host = lib.mkOption { 238 - type = lib.types.nullOr lib.types.str; 239 - default = null; 240 - example = "127.0.0.1"; 241 - description = "Database host address."; 242 - }; 243 - 244 - port = lib.mkOption { 245 - type = lib.types.nullOr lib.types.port; 246 - default = null; 247 - example = 3306; 248 - description = "Database host port."; 249 - }; 250 - 251 - name = lib.mkOption { 252 - type = lib.types.nullOr lib.types.str; 253 - default = null; 254 - example = "headscale"; 255 - description = "Database name."; 256 - }; 257 - 258 - user = lib.mkOption { 259 - type = lib.types.nullOr lib.types.str; 260 - default = null; 261 - example = "headscale"; 262 - description = "Database user."; 263 - }; 264 - 265 - password_file = lib.mkOption { 266 - type = lib.types.nullOr lib.types.path; 267 - default = null; 268 - example = "/run/keys/headscale-dbpassword"; 269 - description = '' 270 - A file containing the password corresponding to 271 - {option}`database.user`. 272 - ''; 273 - }; 274 - }; 275 - }; 276 - 277 - log = { 278 - level = lib.mkOption { 279 - type = lib.types.str; 280 - default = "info"; 281 - description = '' 282 - headscale log level. 283 - ''; 284 - example = "debug"; 285 - }; 286 - 287 - format = lib.mkOption { 288 - type = lib.types.str; 289 - default = "text"; 290 - description = '' 291 - headscale log format. 292 - ''; 293 - example = "json"; 294 - }; 295 - }; 296 - 297 - dns = { 298 - magic_dns = lib.mkOption { 299 - type = lib.types.bool; 300 - default = true; 301 - description = '' 302 - Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/). 303 - ''; 304 - example = false; 305 - }; 306 - 307 - base_domain = lib.mkOption { 308 - type = lib.types.str; 309 - default = ""; 310 - description = '' 311 - Defines the base domain to create the hostnames for MagicDNS. 312 - This domain must be different from the {option}`server_url` 313 - domain. 314 - {option}`base_domain` must be a FQDN, without the trailing dot. 315 - The FQDN of the hosts will be `hostname.base_domain` (e.g. 316 - `myhost.tailnet.example.com`). 317 - ''; 318 - example = "tailnet.example.com"; 319 - }; 320 - 321 - nameservers = { 322 - global = lib.mkOption { 323 - type = lib.types.listOf lib.types.str; 324 - default = [ ]; 325 - description = '' 326 - List of nameservers to pass to Tailscale clients. 327 - ''; 328 - }; 329 - }; 330 - 331 - search_domains = lib.mkOption { 332 - type = lib.types.listOf lib.types.str; 333 - default = [ ]; 334 - description = '' 335 - Search domains to inject to Tailscale clients. 336 - ''; 337 - example = [ "mydomain.internal" ]; 338 - }; 339 - }; 340 - 341 - oidc = { 342 - issuer = lib.mkOption { 343 - type = lib.types.str; 344 - default = ""; 345 - description = '' 346 - URL to OpenID issuer. 347 - ''; 348 - example = "https://openid.example.com"; 349 - }; 350 - 351 - client_id = lib.mkOption { 352 - type = lib.types.str; 353 - default = ""; 354 - description = '' 355 - OpenID Connect client ID. 356 - ''; 357 - }; 358 - 359 - client_secret_path = lib.mkOption { 360 - type = lib.types.nullOr lib.types.str; 361 - default = null; 362 - description = '' 363 - Path to OpenID Connect client secret file. Expands environment variables in format ''${VAR}. 364 - ''; 365 - }; 366 - 367 - scope = lib.mkOption { 368 - type = lib.types.listOf lib.types.str; 369 - default = [ 370 - "openid" 371 - "profile" 372 - "email" 373 - ]; 374 - description = '' 375 - Scopes used in the OIDC flow. 376 - ''; 377 - }; 378 - 379 - extra_params = lib.mkOption { 380 - type = lib.types.attrsOf lib.types.str; 381 - default = { }; 382 - description = '' 383 - Custom query parameters to send with the Authorize Endpoint request. 384 - ''; 385 - example = { 386 - domain_hint = "example.com"; 387 - }; 388 - }; 389 - 390 - allowed_domains = lib.mkOption { 391 - type = lib.types.listOf lib.types.str; 392 - default = [ ]; 393 - description = '' 394 - Allowed principal domains. if an authenticated user's domain 395 - is not in this list authentication request will be rejected. 396 - ''; 397 - example = [ "example.com" ]; 398 - }; 399 - 400 - allowed_users = lib.mkOption { 401 - type = lib.types.listOf lib.types.str; 402 - default = [ ]; 403 - description = '' 404 - Users allowed to authenticate even if not in allowedDomains. 405 - ''; 406 - example = [ "alice@example.com" ]; 407 - }; 408 - }; 409 - 410 - tls_letsencrypt_hostname = lib.mkOption { 411 - type = lib.types.nullOr lib.types.str; 412 - default = ""; 413 - description = '' 414 - Domain name to request a TLS certificate for. 415 - ''; 416 - }; 417 - 418 - tls_letsencrypt_challenge_type = lib.mkOption { 419 - type = lib.types.enum [ 420 - "TLS-ALPN-01" 421 - "HTTP-01" 422 - ]; 423 - default = "HTTP-01"; 424 - description = '' 425 - Type of ACME challenge to use, currently supported types: 426 - `HTTP-01` or `TLS-ALPN-01`. 427 - ''; 428 - }; 429 - 430 - tls_letsencrypt_listen = lib.mkOption { 431 - type = lib.types.nullOr lib.types.str; 432 - default = ":http"; 433 - description = '' 434 - When HTTP-01 challenge is chosen, letsencrypt must set up a 435 - verification endpoint, and it will be listening on: 436 - `:http = port 80`. 437 - ''; 438 - }; 439 - 440 - tls_cert_path = lib.mkOption { 441 - type = lib.types.nullOr lib.types.path; 442 - default = null; 443 - description = '' 444 - Path to already created certificate. 445 - ''; 446 - }; 447 - 448 - tls_key_path = lib.mkOption { 449 - type = lib.types.nullOr lib.types.path; 450 - default = null; 451 - description = '' 452 - Path to key for already created certificate. 453 - ''; 454 - }; 455 - 456 - policy = { 457 - mode = lib.mkOption { 458 - type = lib.types.enum [ 459 - "file" 460 - "database" 461 - ]; 462 - default = "file"; 463 - description = '' 464 - The mode can be "file" or "database" that defines 465 - where the ACL policies are stored and read from. 466 - ''; 467 - }; 468 - 469 - path = lib.mkOption { 470 - type = lib.types.nullOr lib.types.path; 471 - default = null; 472 - description = '' 473 - If the mode is set to "file", the path to a 474 - HuJSON file containing ACL policies. 475 - ''; 476 - }; 477 - }; 478 - }; 479 - }; 480 - }; 481 - }; 482 - }; 483 - 484 - imports = with lib; [ 485 - (mkRenamedOptionModule 486 - [ "services" "headscale" "derp" "autoUpdate" ] 487 - [ "services" "headscale" "settings" "derp" "auto_update_enable" ] 488 - ) 489 - (mkRenamedOptionModule 490 - [ "services" "headscale" "derp" "paths" ] 491 - [ "services" "headscale" "settings" "derp" "paths" ] 492 - ) 493 - (mkRenamedOptionModule 494 - [ "services" "headscale" "derp" "updateFrequency" ] 495 - [ "services" "headscale" "settings" "derp" "update_frequency" ] 496 - ) 497 - (mkRenamedOptionModule 498 - [ "services" "headscale" "derp" "urls" ] 499 - [ "services" "headscale" "settings" "derp" "urls" ] 500 - ) 501 - (mkRenamedOptionModule 502 - [ "services" "headscale" "ephemeralNodeInactivityTimeout" ] 503 - [ "services" "headscale" "settings" "ephemeral_node_inactivity_timeout" ] 504 - ) 505 - (mkRenamedOptionModule 506 - [ "services" "headscale" "logLevel" ] 507 - [ "services" "headscale" "settings" "log" "level" ] 508 - ) 509 - (mkRenamedOptionModule 510 - [ "services" "headscale" "openIdConnect" "clientId" ] 511 - [ "services" "headscale" "settings" "oidc" "client_id" ] 512 - ) 513 - (mkRenamedOptionModule 514 - [ "services" "headscale" "openIdConnect" "clientSecretFile" ] 515 - [ "services" "headscale" "settings" "oidc" "client_secret_path" ] 516 - ) 517 - (mkRenamedOptionModule 518 - [ "services" "headscale" "openIdConnect" "issuer" ] 519 - [ "services" "headscale" "settings" "oidc" "issuer" ] 520 - ) 521 - (mkRenamedOptionModule 522 - [ "services" "headscale" "serverUrl" ] 523 - [ "services" "headscale" "settings" "server_url" ] 524 - ) 525 - (mkRenamedOptionModule 526 - [ "services" "headscale" "tls" "certFile" ] 527 - [ "services" "headscale" "settings" "tls_cert_path" ] 528 - ) 529 - (mkRenamedOptionModule 530 - [ "services" "headscale" "tls" "keyFile" ] 531 - [ "services" "headscale" "settings" "tls_key_path" ] 532 - ) 533 - (mkRenamedOptionModule 534 - [ "services" "headscale" "tls" "letsencrypt" "challengeType" ] 535 - [ "services" "headscale" "settings" "tls_letsencrypt_challenge_type" ] 536 - ) 537 - (mkRenamedOptionModule 538 - [ "services" "headscale" "tls" "letsencrypt" "hostname" ] 539 - [ "services" "headscale" "settings" "tls_letsencrypt_hostname" ] 540 - ) 541 - (mkRenamedOptionModule 542 - [ "services" "headscale" "tls" "letsencrypt" "httpListen" ] 543 - [ "services" "headscale" "settings" "tls_letsencrypt_listen" ] 544 - ) 545 - 546 - (mkRemovedOptionModule [ "services" "headscale" "openIdConnect" "domainMap" ] '' 547 - Headscale no longer uses domain_map. If you're using an old version of headscale you can still set this option via services.headscale.settings.oidc.domain_map. 548 - '') 549 - ]; 550 - 551 - config = lib.mkIf cfg.enable { 552 - assertions = [ 553 - { 554 - assertion = with cfg.settings; dns.magic_dns -> dns.base_domain != ""; 555 - message = "dns.base_domain must be set when using MagicDNS"; 556 - } 557 - (assertRemovedOption [ "settings" "acl_policy_path" ] "Use `policy.path` instead.") 558 - (assertRemovedOption [ "settings" "db_host" ] "Use `database.postgres.host` instead.") 559 - (assertRemovedOption [ "settings" "db_name" ] "Use `database.postgres.name` instead.") 560 - (assertRemovedOption [ 561 - "settings" 562 - "db_password_file" 563 - ] "Use `database.postgres.password_file` instead.") 564 - (assertRemovedOption [ "settings" "db_path" ] "Use `database.sqlite.path` instead.") 565 - (assertRemovedOption [ "settings" "db_port" ] "Use `database.postgres.port` instead.") 566 - (assertRemovedOption [ "settings" "db_type" ] "Use `database.type` instead.") 567 - (assertRemovedOption [ "settings" "db_user" ] "Use `database.postgres.user` instead.") 568 - (assertRemovedOption [ "settings" "dns_config" ] "Use `dns` instead.") 569 - (assertRemovedOption [ "settings" "dns_config" "domains" ] "Use `dns.search_domains` instead.") 570 - (assertRemovedOption [ 571 - "settings" 572 - "dns_config" 573 - "nameservers" 574 - ] "Use `dns.nameservers.global` instead.") 575 - ]; 576 - 577 - services.headscale.settings = lib.mkMerge [ 578 - cliConfig 579 - { 580 - listen_addr = lib.mkDefault "${cfg.address}:${toString cfg.port}"; 581 - 582 - tls_letsencrypt_cache_dir = "${dataDir}/.cache"; 583 - } 584 - ]; 585 - 586 - environment = { 587 - # Headscale CLI needs a minimal config to be able to locate the unix socket 588 - # to talk to the server instance. 589 - etc."headscale/config.yaml".source = cliConfigFile; 590 - 591 - systemPackages = [ cfg.package ]; 592 - }; 593 - 594 - users.groups.headscale = lib.mkIf (cfg.group == "headscale") { }; 595 - 596 - users.users.headscale = lib.mkIf (cfg.user == "headscale") { 597 - description = "headscale user"; 598 - home = dataDir; 599 - group = cfg.group; 600 - isSystemUser = true; 601 - }; 602 - 603 - systemd.services.headscale = { 604 - description = "headscale coordination server for Tailscale"; 605 - wants = [ "network-online.target" ]; 606 - after = [ "network-online.target" ]; 607 - wantedBy = [ "multi-user.target" ]; 608 - 609 - script = '' 610 - ${lib.optionalString (cfg.settings.database.postgres.password_file != null) '' 611 - export HEADSCALE_DATABASE_POSTGRES_PASS="$(head -n1 ${lib.escapeShellArg cfg.settings.database.postgres.password_file})" 612 - ''} 613 - 614 - exec ${lib.getExe cfg.package} serve --config ${configFile} 615 - ''; 616 - 617 - serviceConfig = 618 - let 619 - capabilityBoundingSet = [ "CAP_CHOWN" ] ++ lib.optional (cfg.port < 1024) "CAP_NET_BIND_SERVICE"; 620 - in 621 - { 622 - Restart = "always"; 623 - Type = "simple"; 624 - User = cfg.user; 625 - Group = cfg.group; 626 - 627 - # Hardening options 628 - RuntimeDirectory = "headscale"; 629 - # Allow headscale group access so users can be added and use the CLI. 630 - RuntimeDirectoryMode = "0750"; 631 - 632 - StateDirectory = "headscale"; 633 - StateDirectoryMode = "0750"; 634 - 635 - ProtectSystem = "strict"; 636 - ProtectHome = true; 637 - PrivateTmp = true; 638 - PrivateDevices = true; 639 - ProtectKernelTunables = true; 640 - ProtectControlGroups = true; 641 - RestrictSUIDSGID = true; 642 - PrivateMounts = true; 643 - ProtectKernelModules = true; 644 - ProtectKernelLogs = true; 645 - ProtectHostname = true; 646 - ProtectClock = true; 647 - ProtectProc = "invisible"; 648 - ProcSubset = "pid"; 649 - RestrictNamespaces = true; 650 - RemoveIPC = true; 651 - UMask = "0077"; 652 - 653 - CapabilityBoundingSet = capabilityBoundingSet; 654 - AmbientCapabilities = capabilityBoundingSet; 655 - NoNewPrivileges = true; 656 - LockPersonality = true; 657 - RestrictRealtime = true; 658 - SystemCallFilter = [ 659 - "@system-service" 660 - "~@privileged" 661 - "@chown" 662 - ]; 663 - SystemCallArchitectures = "native"; 664 - RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; 665 - }; 666 - }; 667 - }; 668 - 669 - meta.maintainers = with lib.maintainers; [ 670 - kradalby 671 - misterio77 672 - ]; 673 - }
+5 -7
modules/nixos/logiops.nix
··· 11 11 rendered-config = libconfig-format.generate "logid.cfg" cfg.settings; 12 12 in 13 13 { 14 - options.services.logiops = with lib; { 15 - enable = mkEnableOption (mdDoc "Logiops HID++ configuration"); 14 + options.services.logiops = { 15 + enable = lib.mkEnableOption "Logiops HID++ configuration"; 16 16 17 - package = mkPackageOption pkgs "logiops" { }; 17 + package = lib.mkPackageOption pkgs "logiops_0_2_3" { }; 18 18 19 - settings = mkOption { 19 + settings = lib.mkOption { 20 20 type = libconfig-format.type; 21 21 default = { }; 22 22 example = { ··· 54 54 ]; 55 55 }]; 56 56 }; 57 - description = mdDoc '' 57 + description = lib.mdDoc '' 58 58 Logid configuration. Refer to 59 59 [the `logiops` wiki](https://github.com/PixlOne/logiops/wiki/Configuration) 60 60 for details on supported values. ··· 72 72 restartTriggers = [ rendered-config ]; 73 73 }; 74 74 }; 75 - 76 - meta.maintainers = with lib.maintainers; [ ckie ]; 77 75 }
+1 -1
nixos/fragments/kanata/arsenik.kbd.lisp
··· 59 59 ;; Numrow layer 60 60 (deflayer numrow 61 61 XX XX XX XX XX XX XX XX XX XX XX 62 - XX XX XX XX XX XX XX XX XX XX 62 + XX XX XX XX XX XX XX XX XX XX 63 63 @1 @2 @3 @4 @5 @6 @7 @8 @9 @0 64 64 XX XX XX XX XX XX XX XX XX XX XX 65 65 XX XX XX XX XX
+2 -4
nixos/fragments/logiops.nix
··· 1 1 { self 2 2 , config 3 3 , lib 4 - , pkgs 5 4 , ... 6 5 }: 7 6 ··· 19 18 20 19 config.services.logiops = lib.mkIf cfg.enable { 21 20 enable = true; 22 - package = pkgs.logiops_0_2_3; 23 21 24 22 settings = 25 23 let 26 24 cid = { 27 25 # Control IDs │ reprog? │ fn key? │ mouse key? │ gesture support? 28 - leftMouse = 80; # 0x50 │ │ │ YES │ 29 - rightMouse = 81; # 0x51 │ │ │ YES │ 26 + leftMouse = 80; # 0x50 │ │ │ YES │ 27 + rightMouse = 81; # 0x51 │ │ │ YES │ 30 28 middleMouse = 81; # 0x52 │ YES │ │ YES │ YES 31 29 back = 83; # 0x53 │ YES │ │ YES │ YES 32 30 forward = 86; # 0x56 │ YES │ │ YES │ YES
+7
nixos/fragments/nix.nix
··· 65 65 extra-trusted-public-keys = [ "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" ]; 66 66 }; 67 67 }; 68 + 69 + # other disk space saver 70 + services.angrr = { 71 + enable = true; 72 + period = "2weeks"; 73 + enableNixGcIntegration = true; 74 + }; 68 75 }; 69 76 }
+7 -8
nixos/fragments/security.nix
··· 21 21 security.rtkit.enable = true; 22 22 23 23 # Systemd Login 24 - services.logind = { 25 - lidSwitch = "suspend"; 26 - extraConfig = lib.generators.toKeyValue { } { 27 - IdleAction = "lock"; 28 - # Don’t shutdown when power button is short-pressed 29 - HandlePowerKey = "lock"; 30 - HandlePowerKeyLongPress = "suspend"; 31 - }; 24 + services.logind.settings.Login = { 25 + HandleLidSwitch = "suspend"; 26 + IdleAction = "lock"; 27 + # Don’t shutdown when power button is short-pressed 28 + HandlePowerKey = "lock"; 29 + HandlePowerKeyLongPress = "suspend"; 32 30 }; 33 31 34 32 # `swaylock` pam service must be at least declared to work properly ··· 40 38 # Signing 41 39 programs.gnupg.agent.enable = true; 42 40 services.gnome.gnome-keyring.enable = true; 41 + services.gnome.gcr-ssh-agent.enable = false; 43 42 44 43 # SSH 45 44 services.openssh = {
+27
nixos/hardware/0001-disable-key.patch
··· 1 + From 666eb563eabc461b55b348e997670cfde5e2a0d9 Mon Sep 17 00:00:00 2001 2 + From: Milo Moisson <milo@wiro.world> 3 + Date: Mon, 29 Dec 2025 12:06:29 +0100 4 + Subject: [PATCH] disable key 5 + 6 + --- 7 + src/boot/console.c | 4 ++-- 8 + 1 file changed, 2 insertions(+), 2 deletions(-) 9 + 10 + diff --git a/src/boot/console.c b/src/boot/console.c 11 + index 21b36e5a6e..7ca6664123 100644 12 + --- a/src/boot/console.c 13 + +++ b/src/boot/console.c 14 + @@ -134,8 +134,8 @@ EFI_STATUS console_key_read(uint64_t *ret_key, uint64_t timeout_usec) { 15 + shift |= EFI_CONTROL_PRESSED; 16 + if (keydata.KeyState.KeyShiftState & EFI_ALT_PRESSED) 17 + shift |= EFI_ALT_PRESSED; 18 + - if (keydata.KeyState.KeyShiftState & EFI_LOGO_PRESSED) 19 + - shift |= EFI_LOGO_PRESSED; 20 + + // if (keydata.KeyState.KeyShiftState & EFI_LOGO_PRESSED) 21 + + // shift |= EFI_LOGO_PRESSED; 22 + 23 + /* Shift is not supposed to be reported for keys that can be represented as uppercase 24 + * unicode chars (Shift+f is reported as F instead). Some firmware does it anyway, so 25 + -- 26 + 2.51.2 27 +
+16
nixos/hardware/neo-wiro-laptop.nix
··· 1 1 { config 2 2 , lib 3 + , pkgs 3 4 , ... 4 5 }: 5 6 7 + let 8 + patched-systemd = pkgs.systemd.overrideAttrs (oldAttrs: { 9 + patches = (oldAttrs.patches or [ ]) ++ [ 10 + ./0001-disable-key.patch 11 + ]; 12 + }); 13 + in 6 14 { 7 15 imports = [ ]; 8 16 9 17 config = { 10 18 system.stateVersion = "23.05"; 19 + 20 + systemd.package = patched-systemd; 21 + 22 + services.udev.extraHwdb = '' 23 + evdev:atkbd:dmi:bvn*:bvr*:bd*:sv*:pn*:pvr* 24 + KEYBOARD_KEY_db=reserved 25 + KEYBOARD_KEY_b8=leftmeta 26 + ''; 11 27 12 28 # --- Generated by `nixos-generate-config` --- 13 29 boot.initrd.availableKernelModules = [ "xhci_pci" "thunderbolt" "vmd" "nvme" "sdhci_pci" ];
+6 -19
nixos/profiles/laptop.nix
··· 1 - { self 2 - , config 1 + { config 3 2 , pkgs 4 3 , ... 5 4 }: 6 5 7 - let 8 - inherit (self.outputs) nixosModules; 9 - in 10 6 { 11 - imports = [ 12 - # Replaces nixpkgs module with a custom one that support fallback static location 13 - nixosModules.geoclue2 14 - ]; 15 - 16 7 config = { 17 8 local.fragment = { 18 9 agenix.enable = true; ··· 42 33 kernel.sysctl."kernel.perf_event_paranoid" = -1; 43 34 44 35 kernelPackages = pkgs.linuxKernel.packages.linux_zen; 45 - extraModulePackages = with config.boot.kernelPackages; [ perf xone ]; 36 + extraModulePackages = with config.boot.kernelPackages; [ xone ]; 46 37 47 38 loader = { 48 39 systemd-boot.enable = true; ··· 56 47 57 48 # Once in a while, the session stop job hangs and lasts the full default 58 49 # time (1min30). I just want to shutdown my computer please. 59 - systemd.extraConfig = '' 60 - DefaultTimeoutStopSec = 10s 61 - ''; 50 + systemd.settings.Manager.DefaultTimeoutStopSec = "10s"; 62 51 63 52 programs.dconf.enable = true; 64 53 ··· 86 75 }; 87 76 }; 88 77 89 - programs.command-not-found.enable = false; 90 - 91 78 # This is needed for services like `darkman` and `gammastep` 92 79 services.geoclue2 = { 93 80 enable = true; 94 81 95 - # Fallback using custom geoclue2 module waitng for an alternative to MLS 82 + # Fallback using custom geoclue2 module waiting for an alternative to MLS 96 83 # (Mozilla Location Services). See related module in repo. 97 84 # INFO: lat vvvv vvv long → Paris rough location 98 - staticFile = "48.8\n2.3\n0\n0\n"; 85 + # staticFile = "48.8\n2.3\n0\n0\n"; 99 86 }; 100 87 101 88 programs.wireshark = { ··· 104 91 }; 105 92 users.users.${config.local.user.username}.extraGroups = [ "wireshark" "plugdev" ]; 106 93 107 - # This option is already filled with aliases that snowball and have 94 + # This option is already filled with aliases that snowball and have 108 95 # priority on fish internal `ls` aliases 109 96 environment.shellAliases = { ls = null; ll = null; l = null; }; 110 97 programs.fish.enable = true;
-714
nixos/profiles/server.nix
··· 1 - { self 2 - , config 3 - , pkgs 4 - , upkgs 5 - , ... 6 - }: 7 - 8 - let 9 - inherit (self.inputs) unixpkgs srvos hypixel-bank-tracker tangled; 10 - 11 - json-format = pkgs.formats.json { }; 12 - 13 - ext-if = "eth0"; 14 - external-ip = "91.99.55.74"; 15 - external-netmask = 27; 16 - external-gw = "144.x.x.255"; 17 - external-ip6 = "2a01:4f8:c2c:76d2::1"; 18 - external-netmask6 = 64; 19 - external-gw6 = "fe80::1"; 20 - 21 - well-known-discord-dir = pkgs.writeTextDir ".well-known/discord" '' 22 - dh=919234284ceb2aba439d15b9136073eb2308989b 23 - ''; 24 - webfinger-dir = pkgs.writeTextDir ".well-known/webfinger" '' 25 - { 26 - "subject": "acct:milo@wiro.world", 27 - "aliases": [ 28 - "mailto:milo@wiro.world", 29 - "https://wiro.world/" 30 - ], 31 - "links": [ 32 - { 33 - "rel": "http://wiro.world/rel/avatar", 34 - "href": "https://wiro.world/logo.jpg", 35 - "type": "image/jpeg" 36 - }, 37 - { 38 - "rel": "http://webfinger.net/rel/profile-page", 39 - "href": "https://wiro.world/", 40 - "type": "text/html" 41 - }, 42 - { 43 - "rel": "http://openid.net/specs/connect/1.0/issuer", 44 - "href": "https://auth.wiro.world" 45 - } 46 - ] 47 - } 48 - ''; 49 - 50 - website-hostname = "wiro.world"; 51 - 52 - pds-port = 3001; 53 - pds-hostname = "pds.wiro.world"; 54 - 55 - grafana-port = 3002; 56 - grafana-hostname = "console.wiro.world"; 57 - 58 - tangled-owner = "did:plc:xhgrjm4mcx3p5h3y6eino6ti"; 59 - tangled-knot-port = 3003; 60 - tangled-knot-hostname = "knot.wiro.world"; 61 - tangled-spindle-port = 3004; 62 - tangled-spindle-hostname = "spindle.wiro.world"; 63 - 64 - thelounge-port = 3005; 65 - thelounge-hostname = "lounge.wiro.world"; 66 - 67 - headscale-port = 3006; 68 - headscale-derp-port = 3478; 69 - headscale-hostname = "headscale.wiro.world"; 70 - 71 - lldap-port = 3007; 72 - lldap-hostname = "ldap.wiro.world"; 73 - 74 - authelia-port = 3008; 75 - authelia-hostname = "auth.wiro.world"; 76 - 77 - matrix-port = 3009; 78 - matrix-hostname = "matrix.wiro.world"; 79 - 80 - goatcounter-port = 3010; 81 - goatcounter-hostname = "stats.wiro.world"; 82 - 83 - vaultwarden-port = 3011; 84 - vaultwarden-hostname = "vault.wiro.world"; 85 - 86 - miniflux-port = 3012; 87 - miniflux-hostname = "news.wiro.world"; 88 - 89 - static-hostname = "static.wiro.world"; 90 - 91 - hbt-main-port = 3013; 92 - hbt-banana-port = 3014; 93 - 94 - prometheus-port = 9001; 95 - prometheus-node-exporter-port = 9002; 96 - headscale-metrics-port = 9003; 97 - authelia-metrics-port = 9004; 98 - in 99 - { 100 - disabledModules = [ "services/networking/headscale.nix" ]; 101 - 102 - imports = [ 103 - srvos.nixosModules.server 104 - srvos.nixosModules.hardware-hetzner-cloud 105 - srvos.nixosModules.mixins-terminfo 106 - 107 - self.nixosModules.headscale 108 - 109 - hypixel-bank-tracker.nixosModules.default 110 - 111 - tangled.nixosModules.knot 112 - tangled.nixosModules.spindle 113 - 114 - "${unixpkgs}/nixos/modules/services/matrix/tuwunel.nix" 115 - ]; 116 - 117 - config = { 118 - boot.loader.grub.enable = true; 119 - boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" "ext4" ]; 120 - 121 - # Single network card is `eth0` 122 - networking.usePredictableInterfaceNames = false; 123 - 124 - networking.nameservers = [ "2001:4860:4860::8888" "2001:4860:4860::8844" ]; 125 - 126 - networking = { 127 - interfaces.${ext-if} = { 128 - ipv4.addresses = [{ address = external-ip; prefixLength = external-netmask; }]; 129 - ipv6.addresses = [{ address = external-ip6; prefixLength = external-netmask6; }]; 130 - }; 131 - defaultGateway = { interface = ext-if; address = external-gw; }; 132 - defaultGateway6 = { interface = ext-if; address = external-gw6; }; 133 - 134 - # Reflect firewall configuration on Hetzner 135 - firewall.allowedTCPPorts = [ 22 80 443 ]; 136 - firewall.allowedUDPPorts = [ headscale-derp-port ]; 137 - }; 138 - 139 - services.qemuGuest.enable = true; 140 - 141 - services.openssh.enable = true; 142 - 143 - services.tailscale.enable = true; 144 - 145 - security.sudo.wheelNeedsPassword = false; 146 - 147 - local.fragment.nix.enable = true; 148 - 149 - programs.fish.enable = true; 150 - 151 - services.fail2ban = { 152 - enable = true; 153 - 154 - maxretry = 5; 155 - ignoreIP = [ ]; 156 - 157 - bantime = "24h"; 158 - bantime-increment = { 159 - enable = true; 160 - multipliers = "1 2 4 8 16 32 64"; 161 - maxtime = "168h"; 162 - overalljails = true; 163 - }; 164 - 165 - jails = { }; 166 - }; 167 - 168 - services.caddy = { 169 - enable = true; 170 - 171 - globalConfig = '' 172 - metrics { per_host } 173 - 174 - on_demand_tls { 175 - ask http://localhost:${toString pds-port}/tls-check 176 - } 177 - ''; 178 - 179 - virtualHosts.${website-hostname}.extraConfig = 180 - '' 181 - @discord { 182 - path /.well-known/discord 183 - method GET HEAD 184 - } 185 - route @discord { 186 - header { 187 - Access-Control-Allow-Origin "*" 188 - X-Robots-Tag "noindex" 189 - } 190 - root ${well-known-discord-dir} 191 - file_server 192 - } 193 - '' + 194 - '' 195 - @webfinger { 196 - path /.well-known/webfinger 197 - method GET HEAD 198 - query resource=acct:milo@wiro.world 199 - query resource=mailto:milo@wiro.world 200 - query resource=https://wiro.world 201 - query resource=https://wiro.world/ 202 - } 203 - route @webfinger { 204 - header { 205 - Content-Type "application/jrd+json" 206 - Access-Control-Allow-Origin "*" 207 - X-Robots-Tag "noindex" 208 - } 209 - root ${webfinger-dir} 210 - file_server 211 - } 212 - '' + 213 - '' 214 - reverse_proxy /.well-known/matrix/* http://localhost:${toString matrix-port} 215 - '' + 216 - # TODO: host website on server with automatic deployment 217 - '' 218 - reverse_proxy https://mrnossiom.github.io { 219 - header_up Host {http.request.host} 220 - } 221 - ''; 222 - 223 - virtualHosts.${grafana-hostname}.extraConfig = '' 224 - reverse_proxy http://localhost:${toString grafana-port} 225 - ''; 226 - 227 - virtualHosts.${pds-hostname} = { 228 - serverAliases = [ "*.${pds-hostname}" ]; 229 - extraConfig = '' 230 - tls { on_demand } 231 - reverse_proxy http://localhost:${toString pds-port} 232 - ''; 233 - }; 234 - 235 - virtualHosts.${tangled-knot-hostname}.extraConfig = '' 236 - reverse_proxy http://localhost:${toString tangled-knot-port} 237 - ''; 238 - 239 - virtualHosts.${tangled-spindle-hostname}.extraConfig = '' 240 - reverse_proxy http://localhost:${toString tangled-spindle-port} 241 - ''; 242 - 243 - virtualHosts.${thelounge-hostname}.extraConfig = '' 244 - reverse_proxy http://localhost:${toString thelounge-port} 245 - ''; 246 - 247 - virtualHosts.${headscale-hostname}.extraConfig = '' 248 - reverse_proxy http://localhost:${toString headscale-port} 249 - ''; 250 - 251 - virtualHosts.${lldap-hostname}.extraConfig = '' 252 - reverse_proxy http://localhost:${toString lldap-port} 253 - ''; 254 - 255 - virtualHosts.${authelia-hostname}.extraConfig = '' 256 - reverse_proxy http://localhost:${toString authelia-port} 257 - ''; 258 - 259 - virtualHosts.${matrix-hostname}.extraConfig = '' 260 - reverse_proxy /_matrix/* http://localhost:${toString matrix-port} 261 - ''; 262 - 263 - virtualHosts.${goatcounter-hostname}.extraConfig = '' 264 - reverse_proxy http://localhost:${toString goatcounter-port} 265 - ''; 266 - 267 - virtualHosts.${vaultwarden-hostname}.extraConfig = '' 268 - reverse_proxy http://localhost:${toString vaultwarden-port} 269 - ''; 270 - 271 - virtualHosts.${miniflux-hostname}.extraConfig = '' 272 - reverse_proxy http://localhost:${toString miniflux-port} 273 - ''; 274 - 275 - virtualHosts.${static-hostname}.extraConfig = '' 276 - root /var/www/static 277 - file_server browse 278 - ''; 279 - 280 - virtualHosts."hypixel-bank-tracker.xyz".extraConfig = '' 281 - reverse_proxy http://localhost:${toString hbt-main-port} 282 - ''; 283 - virtualHosts."banana.hypixel-bank-tracker.xyz".extraConfig = '' 284 - reverse_proxy http://localhost:${toString hbt-banana-port} 285 - ''; 286 - }; 287 - 288 - age.secrets.pds-env.file = ../../secrets/pds-env.age; 289 - services.pds = { 290 - enable = true; 291 - package = upkgs.bluesky-pds; 292 - 293 - settings = { 294 - PDS_HOSTNAME = "pds.wiro.world"; 295 - PDS_PORT = pds-port; 296 - # is in systemd /tmp subfolder 297 - LOG_DESTINATION = "/tmp/pds.log"; 298 - }; 299 - 300 - environmentFiles = [ 301 - config.age.secrets.pds-env.path 302 - ]; 303 - }; 304 - 305 - services.tangled.knot = { 306 - enable = true; 307 - openFirewall = true; 308 - 309 - motd = "Welcome to @wiro.world's knot!\n"; 310 - server = { 311 - listenAddr = "localhost:${toString tangled-knot-port}"; 312 - hostname = tangled-knot-hostname; 313 - owner = tangled-owner; 314 - }; 315 - }; 316 - 317 - services.tangled.spindle = { 318 - enable = true; 319 - 320 - server = { 321 - listenAddr = "localhost:${toString tangled-spindle-port}"; 322 - hostname = tangled-spindle-hostname; 323 - owner = tangled-owner; 324 - }; 325 - }; 326 - 327 - age.secrets.grafana-oidc-secret = { file = ../../secrets/grafana-oidc-secret.age; owner = "grafana"; }; 328 - services.grafana = { 329 - enable = true; 330 - 331 - settings = { 332 - server = { 333 - http_port = grafana-port; 334 - domain = grafana-hostname; 335 - root_url = "https://${grafana-hostname}"; 336 - }; 337 - 338 - "auth.generic_oauth" = { 339 - enable = true; 340 - name = "Authelia"; 341 - icon = "signin"; 342 - 343 - client_id = "grafana"; 344 - client_secret_path = config.age.secrets.grafana-oidc-secret.path; 345 - auto_login = true; 346 - 347 - scopes = [ "openid" "profile" "email" "groups" ]; 348 - auth_url = "https://auth.wiro.world/api/oidc/authorization"; 349 - token_url = "https://auth.wiro.world/api/oidc/token"; 350 - api_url = "https://auth.wiro.world/api/oidc/userinfo"; 351 - use_pkce = true; 352 - }; 353 - }; 354 - }; 355 - 356 - services.prometheus = { 357 - enable = true; 358 - port = prometheus-port; 359 - 360 - scrapeConfigs = [ 361 - { 362 - job_name = "caddy"; 363 - static_configs = [{ targets = [ "localhost:${toString 2019}" ]; }]; 364 - } 365 - { 366 - job_name = "node-exporter"; 367 - static_configs = [{ targets = [ "localhost:${toString config.services.prometheus.exporters.node.port}" ]; }]; 368 - } 369 - { 370 - job_name = "headscale"; 371 - static_configs = [{ targets = [ "localhost:${toString headscale-metrics-port}" ]; }]; 372 - } 373 - { 374 - job_name = "authelia"; 375 - static_configs = [{ targets = [ "localhost:${toString authelia-metrics-port}" ]; }]; 376 - } 377 - { 378 - job_name = "miniflux"; 379 - static_configs = [{ targets = [ "localhost:${toString miniflux-port}" ]; }]; 380 - } 381 - ]; 382 - 383 - exporters.node = { 384 - enable = true; 385 - port = prometheus-node-exporter-port; 386 - }; 387 - }; 388 - 389 - services.thelounge = { 390 - enable = true; 391 - port = thelounge-port; 392 - public = false; 393 - 394 - extraConfig = { 395 - host = "127.0.0.1"; 396 - reverseProxy = true; 397 - 398 - # TODO: use ldap, find a way to hide password 399 - }; 400 - }; 401 - 402 - age.secrets.headscale-oidc-secret = { file = ../../secrets/headscale-oidc-secret.age; owner = config.services.headscale.user; }; 403 - # TODO: add dependency on authelia 404 - services.headscale = { 405 - enable = true; 406 - package = upkgs.headscale; 407 - 408 - port = headscale-port; 409 - settings = { 410 - server_url = "https://${headscale-hostname}"; 411 - metrics_listen_addr = "127.0.0.1:${toString headscale-metrics-port}"; 412 - 413 - policy.path = json-format.generate "policy.json" { 414 - acls = [ 415 - { 416 - action = "accept"; 417 - src = [ "autogroup:member" ]; 418 - dst = [ "autogroup:self:*" ]; 419 - } 420 - ]; 421 - ssh = [ 422 - { 423 - action = "accept"; 424 - src = [ "autogroup:member" ]; 425 - dst = [ "autogroup:self" ]; 426 - # Adding root here is privilege escalation as a feature :) 427 - users = [ "autogroup:nonroot" ]; 428 - } 429 - ]; 430 - }; 431 - 432 - # disable TLS 433 - tls_cert_path = null; 434 - tls_key_path = null; 435 - 436 - dns = { 437 - magic_dns = true; 438 - base_domain = "net.wiro.world"; 439 - 440 - override_local_dns = true; 441 - # Quad9 nameservers 442 - nameservers.global = [ "9.9.9.9" "149.112.112.112" "2620:fe::fe" "2620:fe::9" ]; 443 - }; 444 - 445 - oidc = { 446 - only_start_if_oidc_is_available = true; 447 - issuer = "https://auth.wiro.world"; 448 - client_id = "headscale"; 449 - client_secret_path = config.age.secrets.headscale-oidc-secret.path; 450 - scope = [ "openid" "profile" "email" "groups" ]; 451 - pkce.enabled = true; 452 - }; 453 - 454 - derp.server = { 455 - enable = true; 456 - stun_listen_addr = "0.0.0.0:${toString headscale-derp-port}"; 457 - }; 458 - }; 459 - }; 460 - 461 - age.secrets.lldap-env.file = ../../secrets/lldap-env.age; 462 - services.lldap = { 463 - enable = true; 464 - settings = { 465 - http_url = "https://${lldap-hostname}"; 466 - http_port = lldap-port; 467 - 468 - ldap_base_dn = "dc=wiro,dc=world"; 469 - }; 470 - environmentFile = config.age.secrets.lldap-env.path; 471 - }; 472 - 473 - age.secrets.authelia-jwt-secret = { file = ../../secrets/authelia-jwt-secret.age; owner = config.services.authelia.instances.main.user; }; 474 - age.secrets.authelia-issuer-private-key = { file = ../../secrets/authelia-issuer-private-key.age; owner = config.services.authelia.instances.main.user; }; 475 - age.secrets.authelia-storage-key = { file = ../../secrets/authelia-storage-key.age; owner = config.services.authelia.instances.main.user; }; 476 - age.secrets.authelia-ldap-password = { file = ../../secrets/authelia-ldap-password.age; owner = config.services.authelia.instances.main.user; }; 477 - age.secrets.authelia-smtp-password = { file = ../../secrets/authelia-smtp-password.age; owner = config.services.authelia.instances.main.user; }; 478 - services.authelia.instances.main = { 479 - enable = true; 480 - 481 - secrets = { 482 - jwtSecretFile = config.age.secrets.authelia-jwt-secret.path; 483 - oidcIssuerPrivateKeyFile = config.age.secrets.authelia-issuer-private-key.path; 484 - storageEncryptionKeyFile = config.age.secrets.authelia-storage-key.path; 485 - }; 486 - environmentVariables = { 487 - AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE = config.age.secrets.authelia-ldap-password.path; 488 - AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = config.age.secrets.authelia-smtp-password.path; 489 - }; 490 - settings = { 491 - server.address = "localhost:${toString authelia-port}"; 492 - storage.local.path = "/var/lib/authelia-main/db.sqlite3"; 493 - telemetry.metrics = { 494 - enabled = true; 495 - address = "tcp://:${toString authelia-metrics-port}/metrics"; 496 - }; 497 - 498 - session = { 499 - cookies = [{ 500 - domain = "wiro.world"; 501 - authelia_url = "https://${authelia-hostname}"; 502 - default_redirection_url = "https://wiro.world"; 503 - }]; 504 - }; 505 - 506 - authentication_backend.ldap = { 507 - address = "ldap://localhost:3890"; 508 - timeout = "5m"; # replace with systemd dependency 509 - 510 - user = "uid=authelia,ou=people,dc=wiro,dc=world"; 511 - # Set in `AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE`. 512 - # password = ""; 513 - 514 - base_dn = "dc=wiro,dc=world"; 515 - users_filter = "(&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person))"; 516 - additional_users_dn = "ou=people"; 517 - groups_filter = "(&(member={dn})(objectClass=groupOfNames))"; 518 - additional_groups_dn = "ou=groups"; 519 - 520 - attributes = { 521 - username = "uid"; 522 - display_name = "cn"; 523 - given_name = "givenname"; 524 - family_name = "last_name"; 525 - mail = "mail"; 526 - picture = "avatar"; 527 - 528 - group_name = "cn"; 529 - }; 530 - }; 531 - 532 - access_control = { 533 - default_policy = "deny"; 534 - # Rules are sequential and do not apply to OIDC 535 - rules = [ 536 - { 537 - domain = "headscale.wiro.world"; 538 - policy = "two_factor"; 539 - 540 - } 541 - { 542 - domain = "news.wiro.world"; 543 - policy = "one_factor"; 544 - 545 - subject = [ [ "group:miniflux" "oauth2:client:miniflux" ] ]; 546 - } 547 - { 548 - domain = "*.wiro.world"; 549 - policy = "two_factor"; 550 - } 551 - ]; 552 - }; 553 - 554 - identity_providers.oidc = { 555 - enforce_pkce = "always"; 556 - 557 - authorization_policies = 558 - let 559 - mkStrictPolicy = policy: subject: 560 - { default_policy = "deny"; rules = [{ inherit policy subject; }]; }; 561 - in 562 - { 563 - headscale = mkStrictPolicy "two_factor" [ "group:headscale" ]; 564 - tailscale = mkStrictPolicy "two_factor" [ "group:headscale" ]; 565 - grafana = mkStrictPolicy "one_factor" [ "group:grafana" ]; 566 - miniflux = mkStrictPolicy "one_factor" [ "group:miniflux" ]; 567 - }; 568 - 569 - claims_policies.headscale = { id_token = [ "email" "name" "preferred_username" "picture" "groups" ]; }; 570 - 571 - clients = [ 572 - { 573 - client_name = "Headscale"; 574 - client_id = "headscale"; 575 - client_secret = "$pbkdf2-sha256$310000$XY680D9gkSoWhD0UtYHNFg$ptWB3exOYCga6uq1N.oimuV3ILjK3F8lBWBpsBpibos"; 576 - redirect_uris = [ "https://${headscale-hostname}/oidc/callback" ]; 577 - authorization_policy = "headscale"; 578 - claims_policy = "headscale"; 579 - } 580 - { 581 - client_name = "Tailscale"; 582 - client_id = "tailscale"; 583 - client_secret = "$pbkdf2-sha256$310000$PcUaup9aWKI9ZLeCF6.avw$FpsTxkDaxcoQlBi8aIacegXpjEDiCI6nXcaHyZ2Sxyc"; 584 - redirect_uris = [ "https://login.tailscale.com/a/oauth_response" ]; 585 - authorization_policy = "tailscale"; 586 - } 587 - { 588 - client_name = "Grafana Console"; 589 - client_id = "grafana"; 590 - client_secret = "$pbkdf2-sha256$310000$UkwrqxTZodGMs9.Ca2cXAA$HCWFgQbFHGXZpuz.I3HHdkTZLUevRVGlhKEFaOlPmKs"; 591 - redirect_uris = [ "https://${grafana-hostname}/login/generic_oauth" ]; 592 - authorization_policy = "grafana"; 593 - } 594 - { 595 - client_name = "Miniflux"; 596 - client_id = "miniflux"; 597 - client_secret = "$pbkdf2-sha256$310000$uPqbWfCOBXDY6nV1vsx3uA$HOWG2hL.c/bs9Dwaee3b9DxjH7KFO.SaZMbasXV9Vdw"; 598 - redirect_uris = [ "https://${miniflux-hostname}/oauth2/oidc/callback" ]; 599 - authorization_policy = "miniflux"; 600 - } 601 - ]; 602 - }; 603 - 604 - 605 - notifier.smtp = { 606 - address = "smtp://smtp.resend.com:2587"; 607 - username = "resend"; 608 - # Set in `AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE`. 609 - # password = ""; 610 - sender = "authelia@wiro.world"; 611 - }; 612 - }; 613 - }; 614 - 615 - age.secrets.tuwunel-registration-tokens = { file = ../../secrets/tuwunel-registration-tokens.age; owner = config.services.matrix-tuwunel.user; }; 616 - services.matrix-tuwunel = { 617 - enable = true; 618 - package = upkgs.matrix-tuwunel; 619 - 620 - settings.global = { 621 - address = [ "127.0.0.1" ]; 622 - port = [ matrix-port ]; 623 - 624 - server_name = "wiro.world"; 625 - well_known = { 626 - client = "https://matrix.wiro.world"; 627 - server = "matrix.wiro.world:443"; 628 - }; 629 - 630 - grant_admin_to_first_user = true; 631 - new_user_displayname_suffix = ""; 632 - 633 - allow_registration = true; 634 - registration_token_file = config.age.secrets.tuwunel-registration-tokens.path; 635 - }; 636 - }; 637 - 638 - services.goatcounter = { 639 - enable = true; 640 - 641 - port = goatcounter-port; 642 - proxy = true; 643 - extraArgs = [ "-automigrate" ]; 644 - }; 645 - 646 - age.secrets.vaultwarden-env.file = ../../secrets/vaultwarden-env.age; 647 - services.vaultwarden = { 648 - enable = true; 649 - package = upkgs.vaultwarden; 650 - 651 - environmentFile = config.age.secrets.vaultwarden-env.path; 652 - config = { 653 - ROCKET_PORT = vaultwarden-port; 654 - DOMAIN = "https://${vaultwarden-hostname}"; 655 - SIGNUPS_ALLOWED = false; 656 - ADMIN_TOKEN = "$argon2id$v=19$m=65540,t=3,p=4$YIe9wmrTsmjgZNPxe8m34O/d3XW3Fl/uZPPLQs79dAc$mjDVQSdBJqz2uBJuxtAvCIoHPzOnTDhNPuhER3dhHrY"; 657 - 658 - SMTP_HOST = "smtp.resend.com"; 659 - SMTP_PORT = 2465; 660 - SMTP_SECURITY = "force_tls"; 661 - SMTP_USERNAME = "resend"; 662 - # SMTP_PASSWORD = ...; # Via secret env 663 - SMTP_FROM = "bitwarden@wiro.world"; 664 - SMTP_FROM_NAME = "Bitwarden wiro.world"; 665 - }; 666 - }; 667 - 668 - users.users.miniflux = { isSystemUser = true; group = "miniflux"; }; 669 - users.groups.miniflux = { }; 670 - age.secrets.miniflux-oidc-secret = { file = ../../secrets/miniflux-oidc-secret.age; owner = "miniflux"; }; 671 - services.miniflux = { 672 - enable = true; 673 - 674 - createDatabaseLocally = true; 675 - config = { 676 - BASE_URL = "https://${miniflux-hostname}/"; 677 - LISTEN_ADDR = "127.0.0.1:${toString miniflux-port}"; 678 - CREATE_ADMIN = 0; 679 - 680 - METRICS_COLLECTOR = 1; 681 - 682 - OAUTH2_PROVIDER = "oidc"; 683 - OAUTH2_OIDC_PROVIDER_NAME = "wiro.world SSO"; 684 - OAUTH2_CLIENT_ID = "miniflux"; 685 - OAUTH2_CLIENT_SECRET_FILE = config.age.secrets.miniflux-oidc-secret.path; 686 - OAUTH2_REDIRECT_URL = "https://${miniflux-hostname}/oauth2/oidc/callback"; 687 - OAUTH2_OIDC_DISCOVERY_ENDPOINT = "https://auth.wiro.world"; 688 - OAUTH2_USER_CREATION = 1; 689 - DISABLE_LOCAL_AUTH = 1; 690 - 691 - RUN_MIGRATIONS = 1; 692 - 693 - # NetNewsWire is a very good iOS oss client that integrates well 694 - # https://b.j4.lc/2025/05/05/setting-up-netnewswire-with-miniflux/ 695 - }; 696 - }; 697 - 698 - age.secrets.hypixel-bank-tracker-main.file = ../../secrets/hypixel-bank-tracker-main.age; 699 - services.hypixel-bank-tracker.instances.main = { 700 - enable = true; 701 - 702 - port = hbt-main-port; 703 - environmentFile = config.age.secrets.hypixel-bank-tracker-main.path; 704 - }; 705 - 706 - age.secrets.hypixel-bank-tracker-banana.file = ../../secrets/hypixel-bank-tracker-banana.age; 707 - services.hypixel-bank-tracker.instances.banana = { 708 - enable = true; 709 - 710 - port = hbt-banana-port; 711 - environmentFile = config.age.secrets.hypixel-bank-tracker-banana.path; 712 - }; 713 - }; 714 - }
+15 -6
pkgs/default.nix
··· 1 - { self, system, ... }@pkgs: 1 + { self 2 + 3 + , stdenv 4 + , callPackage 5 + , ... 6 + }: 2 7 3 8 let 9 + inherit (stdenv.hostPlatform) system; 10 + 4 11 inherit (self.inputs) 5 12 agenix 6 13 git-leave 14 + nix-alien 7 15 wakatime-ls 8 16 ; 9 17 in 10 18 { 11 - asak = pkgs.callPackage ./asak.nix { }; 12 - ebnfer = pkgs.callPackage ./ebnfer.nix { }; 13 - find-unicode = pkgs.callPackage ./find-unicode.nix { }; 14 - names = pkgs.callPackage ./names.nix { }; 15 - probe-rs-udev-rules = pkgs.callPackage ./probe-rs-udev-rules.nix { }; 19 + asak = callPackage ./asak.nix { }; 20 + ebnfer = callPackage ./ebnfer.nix { }; 21 + find-unicode = callPackage ./find-unicode.nix { }; 22 + names = callPackage ./names.nix { }; 23 + probe-rs-udev-rules = callPackage ./probe-rs-udev-rules.nix { }; 16 24 17 25 # Import packages defined in foreign repositories 18 26 inherit (agenix.packages.${system}) agenix; 19 27 inherit (git-leave.packages.${system}) git-leave; 28 + inherit (nix-alien.packages.${system}) nix-alien; 20 29 inherit (wakatime-ls.packages.${system}) wakatime-ls; 21 30 }
secrets/api-crates-io.age

This is a binary file and will not be displayed.

secrets/authelia-issuer-private-key.age

This is a binary file and will not be displayed.

secrets/authelia-jwt-secret.age

This is a binary file and will not be displayed.

secrets/authelia-ldap-password.age

This is a binary file and will not be displayed.

-9
secrets/authelia-smtp-password.age
··· 1 - age-encryption.org/v1 2 - -> ssh-ed25519 sMF1bg 0duJYaUlZd2G3tYi7CuV+c7KNaqWusLoMpoILvaajgc 3 - AnAlDVzRdeoXGXvyllhSNCoDQjZ+nucLGHCfPWR8IBQ 4 - -> ssh-ed25519 SmMcWg oX82fe4sQmKyjJqv9AFRY6Bww43V/8myNRsN9/M8Sho 5 - Et6ycO8hm2XV36X5q7iO+nJCtjkYoq6mDBEssrjt/70 6 - -> ssh-ed25519 Q8rMFA /gU1tIVjqExV8NXB1gSDsWIXpVfxm3zPJX7xOAqeqWM 7 - szrd67kHWslLO+jMCuDmzYR0LPVVzd7idgl3AKt+USU 8 - --- ojxyQVhx4rX+x5gzEEx8KFiorwywqEEyTNWUJY39jIk 9 - �������ş* l��1����= n�Ŏ��(� �Iamz~B����������� 鰦�.�.Z>�',p��
-11
secrets/authelia-storage-key.age
··· 1 - age-encryption.org/v1 2 - -> ssh-ed25519 sMF1bg PYC6LaFo/DTQlRMewqbVtS4lzVtKyuFZQSqCrQwwEyg 3 - kj0sHy8IpnBsMBQy/XYtj2pLXlk6wprF3MxjFTOiZ6E 4 - -> ssh-ed25519 SmMcWg rvlLGzf6D96Dh1lL8as2vjc3PB7G/86U8AR7/lAELT0 5 - qcdOqVV6o+5OfJYj5pfZ62hOnrBQwY0DAeIJkLoyYss 6 - -> ssh-ed25519 Q8rMFA eALgQVN5OA0EuYYEyEbFnju/Yii71e+xcs98LkXnyyo 7 - Rbxu60qvXKdFaJ7lfs7LeYx37TIKYK2Gxw2QcxF3soA 8 - --- ryt5q5lXn1Cyn4T6RSU1IR7dHZOTbyanaG6ZqeYYGBQ 9 - �G���* \�ʕ�|��� 10 - �S�T���z�! �FIG͍��,��2Q��+�s�76Z�D`��Q�hM�],��E7�2<Q� 11 - ?ׯ�t��u\���xpe/rrR��L}��S��g�%�:�Oj����{T%h��D�~�+DЫ��W%� �ŗ�r�v$%m$
+22
secrets/default.nix
··· 1 + keys: 2 + 3 + let 4 + inherit (keys) sessions systems users; 5 + 6 + nixos = systems ++ users; 7 + home-manager = sessions ++ users; 8 + in 9 + { 10 + # Used in NixOS config 11 + "backup-rclone-googledrive.age".publicKeys = nixos; 12 + "backup-restic-key.age".publicKeys = nixos; 13 + 14 + # Used in Home Manager 15 + "api-crates-io.age".publicKeys = home-manager; 16 + "api-wakatime.age".publicKeys = home-manager; 17 + "api-wakapi.age".publicKeys = home-manager; 18 + 19 + # Not used in config but useful 20 + "pgp-ca5e.age".publicKeys = users; 21 + "ssh-uxgi.age".publicKeys = users; 22 + }
-9
secrets/grafana-oidc-secret.age
··· 1 - age-encryption.org/v1 2 - -> ssh-ed25519 sMF1bg 2+JZ3WMnTgnnNYmJqdqAtOsEIk9pAefHfwLcXZQpqlg 3 - yE0R9b0mSzoT6mvoWUG829UmkgGkm6vg0i7WLORaTis 4 - -> ssh-ed25519 SmMcWg c8goAmCIPQ5rsTwoOej9ndGc0uBpWILSn/wy6XJHK14 5 - ArjlEl4hxi4N6Py1emxgxUFKvSXIwLWsy6HrWNqJUHw 6 - -> ssh-ed25519 Q8rMFA t72V+A0n5Tnb0VqvBrzt3j75CbcfvQltodKMhF64SXY 7 - V+Ynzd4tIUI8JPy9uyoi+frW6wJnQxDavHAqnp/lem8 8 - --- mzRky/dA1MDArnDSSaGyZP9yAQgD2va4MfopbrdM0iU 9 - ��*�٪l����a5�QE£ uަ��"�;��3�l.!` �u� _+)$��d ��ˤ����ff�F��mѳc�a�EV��Nߧ��,�@����
secrets/headscale-oidc-secret.age

This is a binary file and will not be displayed.

secrets/hypixel-bank-tracker-banana.age

This is a binary file and will not be displayed.

-12
secrets/hypixel-bank-tracker-main.age
··· 1 - age-encryption.org/v1 2 - -> ssh-ed25519 sMF1bg B3wX5eWrSUivBKJzdiSOVvWEdGuZvGJHVDCCSFa29kw 3 - F1H0QzBPS1FPvactH7z3OvR5wPdawkMzIj1awUmkrqA 4 - -> ssh-ed25519 SmMcWg DtmTaO1Ox4y+AyZbOgaJU2PxMRdHloHECLOrvdI/g28 5 - my6GKU1rxPyClyos0efuInWok5w3xwwoJ4p3M6roBIw 6 - -> ssh-ed25519 Q8rMFA LKyAN4v78N/OcleCPuYpPlZ0se5Dmp4qiWynY4zTfDo 7 - 2lOqk3QF2lB/b7Md6yWq4R2jn+J3PiATIkrFUxgt104 8 - -> OR`n;K-grease 9 - lWGBER6K10zAcNHyiUMJAnJ84h1ppo5MJ5ztgw 10 - --- abe4RLNH3d9h7UpWD3PRIBgHWagt8cU+AHIFbX1D9/E 11 - �LJu���k؜��_��%��|�J]��KN��?]��tR�<�]�|���ߟ��¾FzU��������:�Y��TX����p,9v6q �7�~�y�]X���͇ǷR5՚>[������#�#�- 12 - cM,1Ǜ"M��"'F�W�3o�
+10 -10
secrets/keys.nix
··· 1 1 rec { 2 2 # Machine SSH key (/etc/ssh/ssh_host_ed25519_key.pub) 3 - archaic = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJDuBHC0f7N0q1KRczJMoaBVdY0JFOtcpPy6WlYsoxUh"; 4 - neo = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINR1/9o1HLnSRkXt3xxAM5So1YCCNdJpBN1leSu7giuR"; 5 - systems = [ archaic neo ]; 3 + archaic-wiro-laptop = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJDuBHC0f7N0q1KRczJMoaBVdY0JFOtcpPy6WlYsoxUh"; 4 + neo-wiro-laptop = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINR1/9o1HLnSRkXt3xxAM5So1YCCNdJpBN1leSu7giuR"; 5 + systems = [ archaic-wiro-laptop neo-wiro-laptop ]; 6 6 7 - weird-row = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5sThvKuIj8yfeZzUPYfxWxnjTTdNtSID2OL4czE8AL"; 8 - servers = [ weird-row ]; 7 + weird-row-server = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5sThvKuIj8yfeZzUPYfxWxnjTTdNtSID2OL4czE8AL"; 8 + servers = [ weird-row-server ]; 9 9 10 10 # Sessions specific age key (~/.ssh/id_home_manager.pub) 11 - neo-milomoisson = "age1vz2zmduaqhaw5jrqh277pmp36plyth8rz5k9ccxeftfcl2nlhalqwvx5xz"; 12 - sessions = [ neo-milomoisson ]; 11 + neo-milo = "age1vz2zmduaqhaw5jrqh277pmp36plyth8rz5k9ccxeftfcl2nlhalqwvx5xz"; 12 + sessions = [ neo-milo ]; 13 13 14 - # User keys (~/.ssh/id_ed25519.pub) 15 - milomoisson = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJdt7atyPTOfaBIsgDYYb0DG1yid2u78abaCDji6Uxgi"; 14 + # User keys (~/.ssh/id_{ed25519,ecdsa}.pub) 15 + milo-ed25519 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJdt7atyPTOfaBIsgDYYb0DG1yid2u78abaCDji6Uxgi"; 16 16 wirody = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMdW6ijH9oTsrswUJmQBF2LQkhjrMFkJ1LktnirPuL2S"; 17 - users = [ milomoisson wirody ]; 17 + users = [ milo-ed25519 wirody ]; 18 18 }
secrets/lldap-env.age

This is a binary file and will not be displayed.

-9
secrets/miniflux-oidc-secret.age
··· 1 - age-encryption.org/v1 2 - -> ssh-ed25519 sMF1bg E4UVPOuq5ZUSxGvIvr0Tod9PQRqDdqHu2Byv4fKi2io 3 - FcdCyfLmRCmK5rmoLQ/m1KOJe9Etu9N/GHCM5lWCIPE 4 - -> ssh-ed25519 SmMcWg wrKv3V6uSLnWQqIp65Rgi0qv7lQtyOXaxnahMo+s3EU 5 - mXsJ1CbS3pzstf3xaWWF150+aXxW2kY2J5kAZWqtl+A 6 - -> ssh-ed25519 Q8rMFA 91npFfTkw9Ur6aZp/pLzLUOIwwPJ9OA1peaZyTlROBU 7 - 12sib8HLjvgN06X6H0/AN4wMewQ8xup813DauZKQ+QY 8 - --- /AGwAMAsPvvuRH6PPNrizBCsJedclYzdj6Kq4V3mx0o 9 - zN��0�=�YP������rլ�!U�n;���n���/��}mCo�F��������ž!z���r������)u��o�3�>��>Z�f�񡤙1Ň ����
secrets/pds-env.age

This is a binary file and will not be displayed.

-42
secrets/secrets.nix
··· 1 - let 2 - inherit (import ./keys.nix) servers sessions systems users; 3 - 4 - nixos = systems ++ users; 5 - home-manager = sessions ++ users; 6 - deploy = servers ++ users; 7 - in 8 - { 9 - # Used in NixOS config 10 - "backup-rclone-googledrive.age".publicKeys = nixos; 11 - "backup-restic-key.age".publicKeys = nixos; 12 - 13 - # Used in Home Manager 14 - "api-crates-io.age".publicKeys = home-manager; 15 - "api-wakatime.age".publicKeys = home-manager; 16 - "api-wakapi.age".publicKeys = home-manager; 17 - 18 - # Used in server deployment 19 - 20 - # Defines `PDS_JWT_SECRET`, `PDS_ADMIN_PASSWORD`, `PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX`, `PDS_EMAIL_SMTP_URL`, `PDS_EMAIL_FROM_ADDRESS`. 21 - "pds-env.age".publicKeys = deploy; 22 - # Defines `LLDAP_JWT_SECRET`, `LLDAP_KEY_SEED`. 23 - "lldap-env.age".publicKeys = deploy; 24 - "headscale-oidc-secret.age".publicKeys = deploy; 25 - "grafana-oidc-secret.age".publicKeys = deploy; 26 - "authelia-jwt-secret.age".publicKeys = deploy; 27 - "authelia-issuer-private-key.age".publicKeys = deploy; 28 - "authelia-storage-key.age".publicKeys = deploy; 29 - "authelia-ldap-password.age".publicKeys = deploy; 30 - "authelia-smtp-password.age".publicKeys = deploy; 31 - "tuwunel-registration-tokens.age".publicKeys = deploy; 32 - # Defines `SMTP_PASSWORD` 33 - "vaultwarden-env.age".publicKeys = deploy; 34 - "miniflux-oidc-secret.age".publicKeys = deploy; 35 - # Defines `HYPIXEL_API_KEY`, `PROFILE_UUID` 36 - "hypixel-bank-tracker-main.age".publicKeys = deploy; 37 - "hypixel-bank-tracker-banana.age".publicKeys = deploy; 38 - 39 - # Not used in config but useful 40 - "pgp-ca5e.age".publicKeys = users; 41 - "ssh-uxgi.age".publicKeys = users; 42 - }
-9
secrets/tuwunel-registration-tokens.age
··· 1 - age-encryption.org/v1 2 - -> ssh-ed25519 sMF1bg TVYRDtTe5khTJo0q8ShrR5o1WBrbK2htHjYCvi5QYAA 3 - kx6Hke5RAZFfugR4aU28SRh4U8e4ymzeIY/+kYlAWhw 4 - -> ssh-ed25519 SmMcWg AyJOM5lQHETeGiI/V5vUtu2vD6PqCZNnuTPvfnU90zE 5 - 9vM7/8JUbScHaeDWig16MgqtULryofSrRqhw2OMWfBs 6 - -> ssh-ed25519 Q8rMFA TeUNtmHquyhhDrXf+zXY56oTGvzkhkaReIoBx5Yb+TE 7 - DLfVy9cO1JrVln9CHV1ag66z2kIMrVzhcaIugLytojE 8 - --- nr/3KZTVXNdemLdmp2bO2bjxKDHvcy3gezZKYN5Z1qI 9 - �"��Z8��ԛ�GvJ�{��(�V9��1�N"��o���o����Х�oJ�~�1�!�}Egd�!�
secrets/vaultwarden-env.age

This is a binary file and will not be displayed.

+9 -2
secrets.nix
··· 1 1 let 2 2 inherit (builtins) listToAttrs attrNames; 3 + 4 + # Map the name and value of all items of an attrset 3 5 mapAttrs' = 4 6 f: 5 7 set: 6 8 listToAttrs (map (attr: f attr set.${attr}) (attrNames set)); 9 + 10 + keys = import ./secrets/keys.nix; 11 + 12 + prependAttrsName = prefix: mapAttrs' (name: value: { name = prefix + name; inherit value; }); 13 + secretsDir = path: prependAttrsName (path + "/") ((import ./${path}/default.nix) keys); 7 14 in 8 15 9 - # You can use agenix directly at repo top-level instead of having to change directory into `secrets/` 10 - mapAttrs' (name: value: { name = ("secrets/" + name); inherit value; }) (import ./secrets/secrets.nix) 16 + secretsDir "secrets" 17 + // secretsDir "hosts/weird-row-server/secrets"
+1 -1
templates/blank/flake.nix
··· 1 1 { 2 2 inputs = { 3 - nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; 3 + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 4 4 }; 5 5 6 6 outputs = { self, nixpkgs }:
+1 -1
templates/c/flake.nix
··· 1 1 { 2 2 inputs = { 3 - nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; 3 + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 4 4 }; 5 5 6 6 outputs = { self, nixpkgs }:
+1 -1
templates/rust/flake.nix
··· 1 1 { 2 2 inputs = { 3 - nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; 3 + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 4 4 5 5 rust-overlay.url = "github:oxalica/rust-overlay"; 6 6 rust-overlay.inputs.nixpkgs.follows = "nixpkgs";
+1 -1
templates/rust-pkg/flake.nix
··· 1 1 { 2 2 inputs = { 3 - nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; 3 + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 4 4 5 5 rust-overlay.url = "github:oxalica/rust-overlay"; 6 6 rust-overlay.inputs.nixpkgs.follows = "nixpkgs";