{
  description = "julm's Nix configurations for hosts (NixOS) and homes (home-manager)";
  /*
    nixConfig = {
    extra-substituters = [
      "https://nix-community.cachix.org"
    ];
    extra-trusted-public-keys = [
      "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
    ];
    };
  */

  inputs = {
    doom-emacs.flake = false;
    doom-emacs.url = "github:hlissner/doom-emacs";
    home-manager.inputs.nixpkgs.follows = "nixpkgs";
    home-manager.url = "github:nix-community/home-manager/release-24.11";
    nix-formatter-pack.inputs.nixpkgs.follows = "nixpkgs";
    nix-formatter-pack.url = "github:Gerschtli/nix-formatter-pack";
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
    git-hooks.inputs.nixpkgs.follows = "nixpkgs";
    git-hooks.inputs.nixpkgs-stable.follows = "nixpkgs";
    git-hooks.url = "github:cachix/git-hooks.nix";
    nixos-hardware.url = "github:NixOS/nixos-hardware/master";
  };

  outputs =
    inputs:
    let
      remoteNixpkgsPatches = import nixpkgs/patches.nix;
      localNixpkgsPatches = [
      ];
      originPkgs = inputs.nixpkgs.legacyPackages."x86_64-linux";
      nixpkgsPath = originPkgs.applyPatches {
        name = "nixpkgs-patched";
        src = inputs.nixpkgs.outPath;
        patches = map originPkgs.fetchpatch remoteNixpkgsPatches ++ localNixpkgsPatches;
        postPatch = ''
          patch=$(printf '%s\n' ${
            builtins.concatStringsSep " " (map (p: p.sha256) remoteNixpkgsPatches ++ localNixpkgsPatches)
          } |
            sort | sha256sum | cut -c -7)
          echo "-patch-$patch" >.version-suffix
        '';
      };
      profile = "/nix/var/nix/profiles/system";
      inherit (inputs.nixpkgs) lib;
      overlays =
        system:
        import nixpkgs/overlays.nix
        ++ [
        ];
      #nixosSystem = lib.nixosSystem;
      nixosSystem = import (nixpkgsPath + "/nixos/lib/eval-config.nix");
      forAllSystems =
        f:
        lib.genAttrs lib.systems.flakeExposed (
          system:
          f rec {
            inherit system;
            #pkgs = inputs.nixpkgs.legacyPackages.${system};
            pkgs = import nixpkgsPath {
              inherit system;
              overlays = overlays system;
            };
          }
        );
      self =
        with lib.fileset;
        toSource {
          root = ./.;
          fileset =
            let
              exts = [
                "clear"
                "conf"
                "cred"
                "crt"
                "css"
                "el"
                "hs"
                "json"
                "nix"
                "patch"
                "pem"
                "pub"
                "sh"
                "theme"
                "vim"
              ];
            in
            unions [
              (fileFilter (file: lib.any file.hasExt exts) ./domains)
              (fileFilter (file: lib.any file.hasExt exts) ./home-manager)
              (fileFilter (file: lib.any file.hasExt exts) ./homes)
              (fileFilter (file: lib.any file.hasExt exts) ./hosts)
              (fileFilter (file: lib.any file.hasExt exts) ./nixos)
              (fileFilter (file: lib.any file.hasExt exts) ./nixpkgs)
              (fileFilter (file: lib.any file.hasExt exts) ./users)
              ./shell.nix
            ];
        };
    in
    rec {
      # nix -L build .#hello
      packages = forAllSystems ({ pkgs, ... }: pkgs);

      # nix -L build .#nixosConfigurations.oignon.config.system.build.toplevel
      # nix -L build .#nixosConfigurations.oignon.config.boot.kernelPackages.kernel.configfile
      # nix -L build .#nixosConfigurations.oignon.pkgs.hello
      # nix eval --raw .#nixosConfigurations.oignon.config.networking.nftables.ruleset
      nixosConfigurations =
        lib.genAttrs
          (builtins.attrNames (
            lib.filterAttrs (_n: v: v == "directory") (builtins.readDir (self + "/hosts"))
          ))
          (
            hostName:
            nixosSystem {
              system = null;
              specialArgs = {
                # Required to avoid infinite recursion
                # when `inputs` is used in `imports`.
                inherit inputs;
              };
              modules = [
                nixos/default.nix
                (import (self + "/hosts/${hostName}.nix"))
                (
                  { config, ... }:
                  {
                    _module.args = {
                      inherit hostName;
                      hosts = nixosConfigurations;
                      host = nixosConfigurations.${hostName}._module.args;
                    };
                    nixpkgs.overlays = overlays config.nixpkgs.hostPlatform.system;
                    nixpkgs.config.permittedInsecurePackages = [
                      # Still needed for chatty
                      # See https://github.com/NixOS/nixpkgs/pull/334638#issuecomment-2289025802%3E
                      # and https://gitlab.gnome.org/World/Chatty/-/issues/932
                      "olm-3.2.16"
                    ];
                    nixpkgs.config.allowUnfreePredicate =
                      pkg:
                      builtins.elem (lib.getName pkg) [
                        "anydesk"
                        "canon-cups-ufr2"
                        "cudatoolkit"
                        "geogebra"
                        "hplip"
                        "memtest86-efi"
                        "nvidia-settings"
                        "nvidia-x11"
                      ];
                  }
                )
                inputs.home-manager.nixosModules.home-manager
                {
                  home-manager.useGlobalPkgs = true;
                  home-manager.useUserPackages = true;
                  home-manager.verbose = true;
                  #home-manager.force = true;
                  home-manager.backupFileExtension = "old";
                  home-manager.extraSpecialArgs = {
                    inherit hostName inputs;
                  };
                }
              ];
            }
          );

      # nix -L develop  or  direnv allow
      devShells = forAllSystems (
        { pkgs, system, ... }:
        {
          default = pkgs.callPackage (self + "/shell.nix") {
            inherit
              pkgs
              inputs
              system
              nixpkgsPath
              ;
            inherit (checks.${system}.git-hooks-check) shellHook;
          };
        }
      );

      # nix -L run .#oignon.switch
      apps = forAllSystems (
        { pkgs, system, ... }:
        with builtins;
        mapAttrs (
          hostName:
          { config, ... }:
          let
            inherit (config.system) build;
            scriptApp = scriptName: ps: script: {
              type = "app";
              program =
                (pkgs.writeShellScript "${hostName}-${scriptName}" ''
                  export PATH="${lib.makeBinPath ([ pkgs.coreutils ] ++ ps)}:$PATH"
                  set -eux
                  ${script}
                '').outPath;
            };
          in
          {
            # Example: nix run .#aubergine.switch
            "switch" = scriptApp "switch" [ ] ''
              shopt -s globstar

              chmod -R g-rwx,o-rwx **/*.gpg
              trap 'git reset **/*.gpg' EXIT
              git rm -rf --cached --ignore-unmatch **/*.gpg # prevent copying to /nix/store

              nix-store --add-root hosts/${hostName}.nixpkgs --indirect --realise ${nixpkgsPath}
              nix-store --add-root hosts/${hostName}.root --indirect --realise ${build.toplevel}

              nix copy --to "ssh://${config.install.target}?''${targetStore-}"${lib.optionalString config.install.substituteOnDestination " --substitute-on-destination"} ${build.toplevel}

              if ssh ${config.install.target} set -x ';' \
                systemctl reset-failed nixos-fallback '2>/dev/null' ';' \
                test "''${NO_NIXOS_FALLBACK:+set}" '||' \
                systemd-run -u nixos-fallback --description=nixos-fallback /bin/sh -xc '''\'''
                  PATH=${
                    with pkgs;
                    lib.makeBinPath [
                      coreutils
                      nix
                      systemd
                    ]
                  }
                  sleep $((10 * 60))
                  ${profile}/bin/switch-to-configuration switch
                  systemctl reboot
                '\'''' '&&' \
                ${build.toplevel}/bin/switch-to-configuration test
              then
                ssh ${config.install.target} -o ControlPath=none set -x ';' \
                  systemctl stop nixos-fallback.service ';' \
                  nix-env --profile ${profile} --set '${build.toplevel}' ';' \
                  ${build.toplevel}/bin/switch-to-configuration boot '&&' \
                  nix-env --delete-generations 7d --profile ${profile}
              else
                tput rev
                echo WARNING: switch-to-configuration was not registered at boot
                tput sgr0
                ssh ${config.install.target} -o ControlPath=none set -x ';' \
                  systemctl stop nixos-fallback.service
                false
              fi
            '';
          }
        ) nixosConfigurations
      );

      # nix flake check
      checks = forAllSystems (
        args: with args; {
          git-hooks-check = inputs.git-hooks.lib.${system}.run {
            src = self;
            hooks = {
              nixfmt-rfc-style.enable = true;
            };
          };
        }
      );

      # nix fmt
      formatter = forAllSystems (
        { pkgs, ... }:
        inputs.nix-formatter-pack.lib.mkFormatter {
          inherit pkgs;
          config = {
            tools = {
              deadnix.enable = true;
              nixfmt-rfc-style.enable = true;
              statix.enable = true;
            };
          };
        }
      );
    };
}