main
  1{
  2  description = "System Config";
  3
  4  nixConfig = {
  5    extra-substituters = [
  6      "https://nixos-raspberrypi.cachix.org"
  7    ];
  8    extra-trusted-public-keys = [
  9      "nixos-raspberrypi.cachix.org-1:4iMO9LXa8BqhU+Rpg6LQKiGa2lsNh/j2oiYLNOQ5sPI="
 10    ];
 11  };
 12
 13  outputs =
 14    { self, ... }@inputs:
 15    let
 16      inherit (self) outputs;
 17      stateVersion = "24.11";
 18
 19      libx = import ./lib {
 20        inherit
 21          self
 22          inputs
 23          outputs
 24          stateVersion
 25          ;
 26      };
 27
 28      supportedSystems = [
 29        "x86_64-linux"
 30        "aarch64-linux"
 31      ];
 32      forAllSystems = inputs.nixpkgs.lib.genAttrs supportedSystems;
 33    in
 34    {
 35      githubActions = inputs.nix-github-actions.lib.mkGithubMatrix {
 36        checks = inputs.nixpkgs.lib.getAttrs [ "x86_64-linux" ] self.packages;
 37      };
 38      githubActionsMatrix = builtins.toJSON (
 39        inputs.nixpkgs.lib.mapAttrsToList
 40          (name: value: {
 41            inherit name;
 42            arch = value._module.specialArgs.system;
 43          })
 44          (
 45            inputs.nixpkgs.lib.attrsets.filterAttrs (
 46              _: config:
 47              (
 48                builtins.hasAttr "system" config._module.specialArgs && config._module.specialArgs.hostname != "foo"
 49              )
 50            ) self.nixosConfigurations
 51          )
 52      );
 53      # Standalone home configurations
 54      # FIXME set this up
 55      homeConfigurations = {
 56        # Fedora CSB workstations
 57        "vdemeest@aomi" = libx.mkHome {
 58          username = "vdemeest";
 59          hostname = "aomi";
 60          desktop = "niri";
 61        };
 62        "vdemeest@kyushu" = libx.mkHome {
 63          username = "vdemeest";
 64          hostname = "kyushu";
 65          desktop = "niri";
 66        };
 67        "vincent@nagoya" = libx.mkHome {
 68          username = "vincent";
 69          hostname = "nagoya";
 70          system = "aarch64-linux";
 71        };
 72        "vincent@sakhalin" = libx.mkHome {
 73          username = "vincent";
 74          hostname = "sakhalin";
 75          standaloneFedora = true;
 76        };
 77        # headless machine
 78        "vincent@aion" = libx.mkHome {
 79          username = "vincent";
 80          hostname = "aion";
 81          system = "aarch64-linux";
 82        };
 83        "houbeb@aion" = libx.mkHome {
 84          username = "houbeb";
 85          hostname = "aion";
 86          system = "aarch64-linux";
 87        };
 88      };
 89      nixosConfigurations = {
 90        # Laptop for LLM workloads (unstable)
 91        okinawa = libx.mkHost {
 92          hostname = "okinawa";
 93          desktop = "niri"; # or "sway"
 94        };
 95        # Servers (unstable)
 96        # sakhalin = libx.mkHost {
 97        #   hostname = "sakhalin";
 98        # };
 99        shikoku = libx.mkHost {
100          hostname = "shikoku";
101          pkgsInput = inputs.nixpkgs-26_05;
102          homeInput = inputs.home-manager-26_05;
103          agenixInput = inputs.agenix-26_05;
104        };
105        # Servers (stable)
106        aix = libx.mkHost {
107          hostname = "aix";
108          system = "aarch64-linux";
109          hardwareType = "rpi4";
110          pkgsInput = inputs.nixpkgs-26_05;
111          homeInput = inputs.home-manager-26_05;
112          agenixInput = inputs.agenix-26_05;
113        };
114        aion = libx.mkHost {
115          hostname = "aion";
116          system = "aarch64-linux";
117          pkgsInput = inputs.nixpkgs-26_05;
118          homeInput = inputs.home-manager-26_05;
119          agenixInput = inputs.agenix-26_05;
120        };
121        rhea = libx.mkHost {
122          hostname = "rhea";
123          system = "aarch64-linux";
124          pkgsInput = inputs.nixpkgs-26_05;
125          homeInput = inputs.home-manager-26_05;
126          agenixInput = inputs.agenix-26_05;
127        };
128        lutece = libx.mkHost {
129          hostname = "lutece";
130          pkgsInput = inputs.nixpkgs-26_05;
131          homeInput = inputs.home-manager-26_05;
132          agenixInput = inputs.agenix-26_05;
133        };
134        carthage = libx.mkHost {
135          hostname = "carthage";
136          pkgsInput = inputs.nixpkgs-26_05;
137          homeInput = inputs.home-manager-26_05;
138          agenixInput = inputs.agenix-26_05;
139        };
140      };
141
142      nixosModules = {
143        # provided modules (to be upstreamed)
144        wireguard-client = ./modules/wireguard/client.nix;
145        wireguard-server = ./modules/wireguard/server.nix;
146        govanityurl = ./modules/govanityurl;
147        gosmee = ./modules/gosmee;
148        rsync-replica = ./modules/rsync-replica;
149        microshift = ./modules/microshift;
150        harmonia = ./modules/harmonia;
151        praetorian = inputs.praetorian.nixosModules.praetorian;
152      };
153
154      # Fedora config files (no system-manager, just the useful files)
155      fedoraConfigs =
156        let
157          mkFedoraConfigs =
158            hostname:
159            let
160              pkgs = import inputs.nixpkgs { system = "x86_64-linux"; };
161              globals = import ./globals.nix {
162                inherit (inputs.nixpkgs) lib;
163                inherit hostname;
164              };
165              fedoraLibx = import ./lib/functions.nix { inherit (inputs.nixpkgs) lib; };
166            in
167            import ./systems/common/fedora-work/gen-configs.nix {
168              inherit (inputs.nixpkgs) lib;
169              inherit pkgs globals hostname;
170              libx = fedoraLibx;
171            };
172        in
173        {
174          kyushu = mkFedoraConfigs "kyushu";
175          aomi = mkFedoraConfigs "aomi";
176          sakhalin =
177            let
178              pkgs = import inputs.nixpkgs { system = "x86_64-linux"; };
179              globals = import ./globals.nix {
180                inherit (inputs.nixpkgs) lib;
181                hostname = "sakhalin";
182              };
183              fedoraLibx = import ./lib/functions.nix { inherit (inputs.nixpkgs) lib; };
184            in
185            import ./systems/sakhalin/gen-configs.nix {
186              inherit pkgs globals;
187              libx = fedoraLibx;
188            };
189        };
190
191      hostConfigs = {
192        nagoya =
193          let
194            pkgs = import inputs.nixpkgs { system = "aarch64-linux"; };
195            globals = import ./globals.nix {
196              inherit (inputs.nixpkgs) lib;
197              hostname = "nagoya";
198            };
199            hostLibx = import ./lib/functions.nix { inherit (inputs.nixpkgs) lib; };
200          in
201          import ./systems/nagoya/gen-configs.nix {
202            inherit pkgs globals;
203            libx = hostLibx;
204          };
205      };
206
207      images = {
208        # sdimages
209        aix =
210          (self.nixosConfigurations.aix.extendModules {
211            modules = [
212              "${inputs.nixpkgs-26_05}/nixos/modules/installer/sd-card/sd-image-aarch64.nix"
213            ];
214          }).config.system.build.sdImage;
215      };
216
217      overlays = import ./overlays { inherit inputs; };
218
219      packages = forAllSystems (
220        system:
221        let
222          pkgs = import inputs.nixpkgs {
223            system = system;
224            config.allowAliases = false;
225            overlays = [
226              self.overlays.additions
227            ];
228          };
229          skipDarwinPackages =
230            system: n:
231            if lib.strings.hasSuffix "darwin" system then !(lib.strings.hasPrefix "koff" n) else true;
232          inherit (inputs.nixpkgs) lib;
233          drvAttrs = builtins.filter (n: lib.isDerivation pkgs.${n} && skipDarwinPackages system n) (
234            builtins.attrNames (self.overlays.additions pkgs pkgs)
235          );
236        in
237        lib.listToAttrs (map (n: lib.nameValuePair n pkgs.${n}) drvAttrs)
238        // {
239          dns-zones =
240            let
241              globals = import ./globals.nix { };
242              dns = inputs.dns;
243              mkZoneFile =
244                zoneName: zoneFile:
245                pkgs.writeText "db.${zoneName}" (
246                  dns.lib.toString zoneName (import zoneFile { inherit dns globals; })
247                );
248            in
249            pkgs.runCommand "dns-zones" { } ''
250              mkdir -p $out
251              cp ${mkZoneFile "sbr.pm" ./systems/common/services/dns/sbr.pm.nix} $out/db.sbr.pm
252              cp ${mkZoneFile "home" ./systems/common/services/dns/home.nix} $out/db.home
253              cp ${mkZoneFile "vpn" ./systems/common/services/dns/vpn.nix} $out/db.vpn
254              cp ${mkZoneFile "demeester.fr" ./systems/common/services/dns/demeester.fr.nix} $out/db.demeester.fr
255              cp ${mkZoneFile "192.168.1.in-addr.arpa" ./systems/common/services/dns/192.168.1.nix} $out/db.192.168.1
256              cp ${mkZoneFile "10.100.0.in-addr.arpa" ./systems/common/services/dns/10.100.0.nix} $out/db.10.100.0
257            '';
258
259          wg-configs =
260            let
261              globals = import ./globals.nix { };
262              serverPubkey = globals.machines.carthage.net.vpn.pubkey;
263              endpoint = globals.net.vpn.endpoint;
264              mkWgConfig =
265                hostname:
266                let
267                  machine = globals.machines.${hostname};
268                  vpnIp = builtins.head machine.net.vpn.ips;
269                in
270                pkgs.writeText "wg0-${hostname}.conf" ''
271                  [Interface]
272                  Address = ${vpnIp}/32
273                  PostUp = test -f /persist/wireguard/private.key && wg set %i private-key /persist/wireguard/private.key || true
274
275                  [Peer]
276                  PublicKey = ${serverPubkey}
277                  Endpoint = ${endpoint}:51820
278                  AllowedIPs = 10.100.0.0/24
279                  PersistentKeepalive = 25
280                '';
281            in
282            pkgs.runCommand "wg-configs" { } ''
283              mkdir -p $out
284              cp ${mkWgConfig "aix"} $out/wg0-aix.conf
285              cp ${mkWgConfig "athena"} $out/wg0-athena.conf
286              cp ${mkWgConfig "demeter"} $out/wg0-demeter.conf
287            '';
288        }
289      );
290
291      checks = forAllSystems (system: {
292        pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run {
293          src = ./.;
294          # Run hooks on pre-push instead of pre-commit for less intrusive workflow
295          default_stages = [
296            "manual"
297            "pre-push"
298          ];
299          hooks = {
300            # go
301            gofmt.enable = true;
302            # golangci-lint.enable = true;
303            # nix
304            deadnix.enable = true;
305            nixfmt.enable = true;
306            # statix.enable = true;
307            # python
308            ruff.enable = true;
309            # shell
310            shellcheck = {
311              enable = true;
312              excludes = [ "dots/config/zsh/.*" ]; # zsh files use zsh-specific syntax
313            };
314            # emacs lisp - basic syntax checking
315            elisp-byte-compile = {
316              enable = false; # Disabled - causes issues with missing packages in pre-push hook
317              name = "Emacs Lisp byte-compile";
318              entry =
319                let
320                  pkgs = import inputs.nixpkgs { system = system; };
321                  # Create a wrapper that runs Emacs byte-compilation
322                  elisp-check = pkgs.writeShellScript "elisp-check" ''
323                    ${pkgs.emacs}/bin/emacs --batch \
324                      --eval "(setq byte-compile-error-on-warn t)" \
325                      -f batch-byte-compile "$@"
326                  '';
327                in
328                toString elisp-check;
329              files = "\\.el$";
330              excludes = [
331                "dots/\\.config/emacs/old/.*"
332                "dots/\\.config/emacs/elpa/.*"
333                "dots/\\.config/emacs/transient/.*"
334                "dots/\\.config/emacs/eshell/.*"
335                "dots/\\.config/emacs/custom\\.el"
336                "dots/\\.config/emacs/\\.chatgpt-shell\\.el"
337                "dots/\\.config/emacs/site-lisp/.*"
338                "\\.dir-locals\\.el"
339              ];
340            };
341          };
342        };
343      });
344
345      devShells = forAllSystems (system: {
346        default =
347          let
348            pkgs = import inputs.nixpkgs {
349              system = system;
350              config.allowUnfree = true;
351            };
352          in
353          inputs.nixpkgs.legacyPackages.${system}.mkShell {
354            inherit (self.checks.${system}.pre-commit-check) shellHook;
355            buildInputs = self.checks.${system}.pre-commit-check.enabledPackages;
356            packages = [
357              pkgs.git
358              pkgs.prettier
359              pkgs.deadnix
360              pkgs.nixfmt
361              inputs.agenix.packages.${system}.default
362            ];
363            name = "home";
364            DIRENV_LOG_FORMAT = "";
365          };
366      });
367    };
368
369  inputs = {
370    # Flake for compatibility with non-flake commands
371    flake-compat = {
372      type = "github";
373      owner = "edolstra";
374      repo = "flake-compat";
375      flake = false;
376    };
377
378    buildkit-tekton = {
379      url = "github:vdemeester/buildkit-tekton";
380      inputs.nixpkgs.follows = "nixpkgs";
381    };
382    flake-parts.url = "github:hercules-ci/flake-parts";
383    go-org-readwise = {
384      url = "git+https://codeberg.org/vdemeester/go-org-readwise";
385      inputs.nixpkgs.follows = "nixpkgs";
386      inputs.flake-parts.follows = "flake-parts";
387    };
388    pass-run = {
389      url = "github:vdemeester/pass-run/v0.1.0";
390      inputs.nixpkgs.follows = "nixpkgs";
391    };
392    radian = {
393      url = "github:vdemeester/radian";
394      inputs.nixpkgs.follows = "nixpkgs";
395      inputs.flake-parts.follows = "flake-parts";
396    };
397
398    # nixpkgs
399    nixpkgs = {
400      type = "github";
401      owner = "NixOS";
402      repo = "nixpkgs";
403      ref = "nixos-unstable";
404    };
405    nixpkgs-26_05 = {
406      type = "github";
407      owner = "NixOS";
408      repo = "nixpkgs";
409      ref = "nixos-26.05";
410    };
411    nixpkgs-master.url = "github:nixos/nixpkgs/master";
412    nono = {
413      url = "github:nolabs-ai/nono/v0.78.0";
414      inputs.nixpkgs.follows = "nixpkgs";
415    };
416    nixpkgs-wip-consolidated = {
417      type = "github";
418      owner = "vdemeester";
419      repo = "nixpkgs";
420      ref = "wip-consolidated";
421    };
422    pre-commit-hooks.url = "github:cachix/git-hooks.nix";
423    pre-commit-hooks.inputs.nixpkgs.follows = "nixpkgs";
424    pre-commit-hooks.inputs.flake-compat.follows = "flake-compat";
425    # Home Manager
426    home-manager = {
427      type = "github";
428      owner = "nix-community";
429      repo = "home-manager";
430      inputs.nixpkgs.follows = "nixpkgs";
431    };
432    dns = {
433      url = "github:nix-community/dns.nix";
434      inputs.nixpkgs.follows = "nixpkgs";
435    };
436    home-manager-26_05 = {
437      type = "github";
438      owner = "nix-community";
439      repo = "home-manager";
440      ref = "release-26.05";
441      inputs.nixpkgs.follows = "nixpkgs-26_05";
442    };
443
444    emacs-overlay = {
445      url = "github:nix-community/emacs-overlay";
446      inputs.nixpkgs.follows = "nixpkgs";
447      inputs.nixpkgs-stable.follows = "nixpkgs-26_05";
448    };
449
450    nixos-hardware = {
451      type = "github";
452      owner = "NixOS";
453      "repo" = "nixos-hardware";
454    };
455
456    # Me :D
457    chick-group = {
458      type = "github";
459      owner = "vdemeester";
460      repo = "chick-group";
461      inputs.nixpkgs.follows = "nixpkgs";
462      inputs.flake-parts.follows = "flake-parts";
463      inputs.pre-commit-hooks.follows = "pre-commit-hooks";
464    };
465    # Red Hat
466    chapeau-rouge = {
467      type = "github";
468      owner = "vdemeester";
469      repo = "chapeau-rouge";
470      inputs.nixpkgs.follows = "nixpkgs";
471      inputs.flake-parts.follows = "flake-parts";
472      inputs.pre-commit-hooks.follows = "pre-commit-hooks";
473    };
474    agenix.url = "github:ryantm/agenix";
475    agenix.inputs.nixpkgs.follows = "nixpkgs";
476    agenix.inputs.home-manager.follows = "home-manager";
477    agenix-26_05.url = "github:ryantm/agenix";
478    agenix-26_05.inputs.nixpkgs.follows = "nixpkgs-26_05";
479    agenix-26_05.inputs.home-manager.follows = "home-manager-26_05";
480
481    nix-cachyos-kernel = {
482      url = "github:xddxdd/nix-cachyos-kernel";
483      inputs.nixpkgs.follows = "nixpkgs";
484    };
485
486    lanzaboote.url = "github:nix-community/lanzaboote";
487    lanzaboote.inputs.nixpkgs.follows = "nixpkgs";
488
489    disko.url = "github:nix-community/disko";
490    disko.inputs.nixpkgs.follows = "nixpkgs";
491
492    harmonia.url = "github:nix-community/harmonia";
493    harmonia.inputs.nixpkgs.follows = "nixpkgs";
494
495    nix-github-actions.url = "github:nix-community/nix-github-actions";
496    nix-github-actions.inputs.nixpkgs.follows = "nixpkgs";
497
498    nixos-raspberrypi.url = "github:nvmd/nixos-raspberrypi/develop";
499    nixos-raspberrypi.inputs.nixpkgs.follows = "nixpkgs";
500    nixos-raspberrypi.inputs.flake-compat.follows = "flake-compat";
501
502    llm-agents.url = "github:numtide/llm-agents.nix";
503    llm-agents.inputs.nixpkgs.follows = "nixpkgs";
504    llm-agents.inputs.flake-parts.follows = "flake-parts";
505
506    voxtype.url = "github:peteonrails/voxtype/main";
507    voxtype.inputs.nixpkgs.follows = "nixpkgs";
508
509    praetorian.url = "github:vdemeester/praetorian/v2.0.0-rc2";
510    praetorian.inputs.nixpkgs.follows = "nixpkgs";
511  };
512}