{
  inputs = {
    nixpkgs.url = "git+ssh://git@github.com/nixos/nixpkgs.git?shallow=1&ref=nixos-24.11";
    nixpkgs-unstable.url = "git+https://github.com/nixos/nixpkgs?ref=nixpkgs-unstable";

    # nix darwin version must match nixpkgs version:
    #   nixpkgs:    nixos-xx.yy
    #   nix-darwin: nix-darwin-xx.yy
    # with xx.yy being the same
    darwin.url = "git+https://github.com/lnl7/nix-darwin?ref=nix-darwin-24.11";
    darwin.inputs.nixpkgs.follows = "nixpkgs";

    deploy-rs.url = "git+https://github.com/serokell/deploy-rs?ref=master";
    deploy-rs.inputs.nixpkgs.follows = "nixpkgs";

    agenix.url = "git+https://github.com/ryantm/agenix";
    agenix.inputs.nixpkgs.follows = "nixpkgs";

    home-manager.url = "git+https://github.com/nix-community/home-manager?ref=release-24.11";
    home-manager.inputs.nixpkgs.follows = "nixpkgs";

    naersk.url = "git+https://github.com/nix-community/naersk";
    naersk.inputs.nixpkgs.follows = "nixpkgs";

    flake-utils.url = "git+https://github.com/numtide/flake-utils";

    # hefe-internal.url = "git+file:///Users/emile/hefe-internal";
    # hefe-internal.url = "git+ssh://git@git.emile.space/hefe-internal";

    # nix registry add flake:mylocalrepo git+file:///path/to/local/repo
    # nix registry list
    # hefe-internal.url = "flake:mylocalrepo";
  };

  outputs =
    {
      self,
      nixpkgs, # packages
      nixpkgs-unstable, # unstable branch
      darwin, # darwin related stuff
      deploy-rs, # deploy the hosts
      agenix, # store secrets crypted using age
      home-manager, # manage my home envs
      naersk, # build rust stuff
      flake-utils, # common flake utils
      # hefe-internal,    # internal tooling
      ...
    }@inputs:
    let
      lib = import ./nix/lib inputs;
      helper = lib.flake-helper;

      # TODO(emile): move all these functions into the helper, keeping the flake.nix clean

      # A function taking an attribute set of flake templates, importing their
      # flake.nix/output/packages (if there are any) and returning an attribute set
      # of their packages (if the template has one or more)
      template-packages =
        builtins.mapAttrs (name: value:
          (((import ./nix/templates/${name}/flake.nix).outputs)
            { inherit nixpkgs flake-utils; })
            .packages or { }
        );

      # apply the above function to the templates
      templates = template-packages self.templates;

      # Merge template packages into root packages with template prefix
      merged-template-packages =
        system:
        let
          lib = nixpkgs.lib;
        in
        lib.foldl (
          acc: tplName:
          let
            tplPkgs = templates.${tplName}.${system} or { };
            prefixed = lib.mapAttrs' (pkgName: pkg: lib.nameValuePair "${tplName}-${pkgName}" pkg) tplPkgs;
          in
          acc // prefixed
        ) { } (builtins.attrNames templates);
    in
    {
      hosts = {
        caladan = {
          # in /etc/nix/machines
          # ssh://corrino.emile.space x86_64-linux - 16 2 nixos-test,benchmark,big-parallel,kvm - -
          system = "aarch64-darwin";
          sshUser = "hydra";
          homeManagerEnable = true;
          description = "macbook air";
          # nix run https://github.com/LnL7/nix-darwin/archive/master.tar.gz -- switch --flake .#caladan
        };

        # main vm host
        #
        # in case of broken config, reboot into recovery, then:
        #
        # cryptsetup luksOpen /dev/md1 luks0
        # mount /dev/disk/by-label/root /mnt
        # mkdir /mnt/boot
        # mount /dev/disk/by-label/root /mnt
        # grub-reboot --boot-directory /mnt/boot "Ni" <- press tab and choose wisely
        #
        # also see
        # //nix/hosts/corrino/hetzner-dedicated-wipe-and-install-nixos-luks-raid-lvm.sh
        corrino = {
          system = "x86_64-linux";
          ip = "corrino";
          description = "Hetzner AX41 dual 512GB NVME";
          modules = [
            # hefe-internal.nixosModules.corrino
            (
              { self, ... }:
              {
                nixpkgs.overlays = [
                  (final: prev: {
                    inherit (self.packages.x86_64-linux)
                      goapp-frontend
                      ;
                  })
                ];
              }
            )
          ];
        };
        chusuk = {
          # ip = "chusuk.pinto-pike.ts.net";
          system = "x86_64-linux";
          description = "lenovo t480";
        };
        hacknix = {
          # ip = "hacknix.pinto-pike.ts.net"; # clone repo and deploy within
          system = "x86_64-linux";
          description = "hacking vm";
        };
        mail = {
          # ip = "mail.pinto-pike.ts.net"; # clone repo and deploy within
          system = "x86_64-linux";
          description = "mail server";
        };

        #kolhar = {}; # nixos vm on caladan
        #hagal = {}; # apple tv

        lampadas = {
          system = "x86_64-linux"; # 4c4t (intel n100), 32GB RAM
          description = "NAS";
        };
        # palma = {
        #   description = "palma bmc";
        # };

        lernaeus = {
          system = "x86_64-linux"; # 8c16t (AMD ryzen 5 5600g), 32GB RAM
          description = "VM Host";
          ip = "192.168.1.79";
        };
        # parmentier = {
        #   description = "lernaeus bmc";
        # };

        # lankiveil = {
        #   system = "x86_64-linux"; # ???, ???, RTX A2000
        #   description = "Router";
        # };
        # poritrin = {
        #   description = "lankiveil bmc";
        # };

        # kaitain = {};
        # ecaz = {};
        # gamont = {};

        # futher names: https://neoencyclopedia.fandom.com/wiki/List_of_Dune_planets
        # Muritan
        # Naraj
        # Palma
        # Parmentier
        # Poritrin
        # Richese
        # Romo
        # Rossak
        # Sikun
        # Synchrony
        # Tleilax
        # Tupile
        # Zanovar
      };

      nixosConfigurations = helper.mapToNixosConfigurations self.hosts;
      darwinConfigurations = helper.mapToDarwinConfigurations self.hosts;

      nixosModules = {
        x86_64-linux = import ./nix/modules/x86_64-linux.nix;
        default = self.nixosModules.x86_64-linux;
      };

      overlays = {
        default = self.overlays.x86_64-linux;

        x86_64-linux = import ./nix/pkgs/x86_64-linux.nix;
        aarch64-darwin = import ./nix/pkgs/aarch64-darwin.nix;

        unstable = final: prev: {
          unstable = import nixpkgs-unstable {
            system = "x86_64-linux";
            config.allowUnfree = true;
          };
        };

        # no clue why, but when rebuilding corrino and this not being commented,
        # something in the hardware.bluetooth module breaks
        #
        # unstable-darwin = final: prev: {
        #   unstable-darwin = import nixpkgs-unstable {
        #     system = "aarch64-darwin";
        #     config.allowUnfree = true;
        #   };
        # };
      };

      deploy.nodes = helper.mapToDeployRsConfiguration self.hosts;
      deploy.autoRollback = true;

      packages =
        nixpkgs.lib.genAttrs
          [
            "x86_64-linux"
            "aarch64-darwin"
          ]
          (
            system:
            let
              pkgs = import nixpkgs {
                inherit system;
                overlays = [
                  (
                    if system == "x86_64-linux" then
                      self.overlays.x86_64-linux
                    else if system == "aarch64-darwin" then
                      self.overlays.aarch64-darwin
                    else
                      null
                  )
                  # some arguments for packages
                  (_: _: { inherit naersk; })
                ];
              };
            in
            # take all the packages exposed from templates and add them to
            # the packages exposed by this flake
            merged-template-packages system
            // {
              inherit (pkgs) vokobe r2wars-web remarvin;
            }
          );

      hydraJobs = {
        inherit (self) packages;
        nixosConfigurations = helper.buildHosts self.nixosConfigurations;
        templates = template-packages self.templates;
      };

      templates = {
        # ; nix nix registry add hefe /Users/emile/Documents/hefe
        # ; nix flake init -t hefe#ctf
        ctf = {
          description = "A basic ctf env with pwn, rev, ... tools";
          path = ./nix/templates/ctf;
          welcomeText = ''
            # CTF flake template

            Run `nix develop` to get a shell with pwntools, pwndbg, pycryptodome, ...

            Add packages in the flake as you like.
          '';
        };
        goapp = {
          description = "A basic golang service";
          path = ./nix/templates/goapp;
          welcomeText = ''
            # A basic golang service
             
            - using gorilla/mux
          '';
        };

        # checks = builtins.mapAttrs (system: deployLib:
        #     deployLib.deployChecks self.deploy) deploy-rs.lib;
      };
    };
}