{
  description = "C3D2 NixOS configurations";

  nixConfig = {
    extra-substituters = [ "https://nix-cache.hq.c3d2.de" ];
    extra-trusted-public-keys = [ "nix-cache.hq.c3d2.de:KZRGGnwOYzys6pxgM8jlur36RmkJQ/y8y62e52fj1ps=" ];
  };

  inputs = {
    # use sandro's fork full with cherry-picked fixes
    nixos.url = "github:SuperSandro2000/nixpkgs/nixos-22.11";
    # nixos-mobilizon.url = "github:minijackson/nixpkgs/init-mobilizon";
    nixos-hardware.url = "github:nixos/nixos-hardware";
    # nixos-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
    # nixos-unstable-simd.url = "github:SuperSandro2000/nixpkgs/nixos-unstable-simd";

    affection-src = {
      url = "git+https://gitea.nek0.eu/nek0/affection";
      inputs = {
        nixpkgs.follows = "nixos";
        flake-utils.follows = "flake-utils";
      };
    };
    alert2muc = {
      url = "git+https://gitea.c3d2.de/astro/alert2muc";
      inputs = {
        naersk.follows = "naersk";
        nixpkgs.follows = "nixos";
        utils.follows = "flake-utils";
      };
    };
    bevy-mandelbrot = {
      # url = "github:matelab/bevy_mandelbrot";
      url = "git+https://gitea.c3d2.de/astro/bevy-mandelbrot.git?ref=main";
      inputs = {
        naersk.follows = "naersk";
        nixpkgs.follows = "nixos";
        rust-overlay.follows = "rust-overlay";
      };
    };
    bevy-julia = {
      # url = "github:matelab/bevy_julia";
      url = "git+https://gitea.c3d2.de/astro/bevy-julia.git?ref=main";
      inputs = {
        nixpkgs.follows = "nixos";
        naersk.follows = "naersk";
        rust-overlay.follows = "rust-overlay";
      };
    };
    buzzrelay = {
      url = "github:astro/buzzrelay";
      inputs = {
        naersk.follows = "naersk";
        nixpkgs.follows = "nixos";
        utils.follows = "flake-utils";
      };
    };
    caveman = {
      url = "git+https://gitea.c3d2.de/astro/caveman.git?ref=main";
      inputs = {
        nixpkgs.follows = "nixos";
        utils.follows = "flake-utils";
        fenix.follows = "fenix";
        fenix.inputs.nixpkgs.follows = "nixpkgs";
        naersk.follows = "naersk";
        naersk.inputs.nixpkgs.follows = "nixpkgs";
      };
    };
    c3d2-user-module = {
      url = "git+https://gitea.c3d2.de/c3d2/nix-user-module.git";
      inputs = {
        nixos-modules.follows = "nixos-modules";
        nixpkgs-lib.follows = "nixos";
      };
    };
    deployment = {
      url = "git+https://gitea.c3d2.de/c3d2/deployment.git";
      inputs = {
        zentralwerk.follows = "zentralwerk";
      };
    };
    fenix = {
      url = "github:nix-community/fenix/monthly";
      inputs.nixpkgs.follows = "nixos";
    };
    flake-parts = {
      url = "github:hercules-ci/flake-parts";
      inputs.nixpkgs-lib.follows = "nixos";
    };
    flake-utils.url = "github:numtide/flake-utils";
    # TODO: drop when switching to 23.05 and check if treefmt-nix is still used
    harmonia = {
      url = "github:SuperSandro2000/harmonia/flake-lock";
      inputs = {
        flake-parts.follows = "flake-parts";
        nixpkgs.follows = "nixos";
        treefmt-nix.follows = "treefmt-nix";
      };
    };
    heliwatch = {
      url = "git+https://gitea.c3d2.de/astro/heliwatch.git";
      inputs = {
        fenix.follows = "fenix";
        nixpkgs.follows = "nixos";
        naersk.follows = "naersk";
        utils.follows = "flake-utils";
      };
    };
    # hydra-ca.url = "github:mlabs-haskell/hydra/aciceri/ca-derivations";
    microvm = {
      url = "github:astro/microvm.nix";
      inputs = {
        flake-utils.follows = "flake-utils";
        nixpkgs.follows = "nixos";
      };
    };
    naersk = {
      url = "github:nix-community/naersk";
      inputs = {
        nixpkgs.follows = "nixos";
      };
    };
    nixos-modules = {
      url = "github:SuperSandro2000/nixos-modules";
      inputs.nixpkgs-lib.follows = "nixos";
    };
    oparl-scraper = {
      url = "github:offenesdresden/ratsinfo-scraper/oparl";
      flake = false;
    };
    openwrt = {
      url = "git+https://git.openwrt.org/openwrt/openwrt.git?ref=openwrt-21.02";
      flake = false;
    };
    openwrt-imagebuilder = {
      url = "github:astro/nix-openwrt-imagebuilder";
      inputs = {
        nixpkgs.follows = "nixos";
      };
    };
    rust-overlay = {
      url = "github:oxalica/rust-overlay/stable";
      inputs = {
        flake-utils.follows = "flake-utils";
        nixpkgs.follows = "nixos";
      };
    };
    scrapers = {
      url = "git+https://gitea.c3d2.de/astro/scrapers.git";
      flake = false;
    };
    skyflake = {
      url = "github:astro/skyflake";
      inputs = {
        microvm.follows = "microvm";
        nixpkgs.follows = "nixos";
      };
    };
    sshlogd = {
      url = "git+https://gitea.c3d2.de/astro/sshlogd.git?ref=main";
      inputs = {
        utils.follows = "flake-utils";
        naersk.follows = "naersk";
        nixpkgs.follows = "nixos";
        fenix.follows = "fenix";
      };
    };
    # deprecated
    secrets.url = "git+ssh://gitea@gitea.c3d2.de/c3d2-admins/secrets.git";
    sops-nix = {
      url = "github:Mic92/sops-nix";
      inputs = {
        nixpkgs.follows = "nixos";
        nixpkgs-stable.follows = "nixos";
      };
    };
    spacemsg = {
      url = "github:astro/spacemsg";
      flake = false;
    };
    ticker = {
      url = "git+https://gitea.c3d2.de/astro/ticker.git";
      inputs = {
        fenix.follows = "fenix";
        # causes build failure
        # naersk.follows = "naersk";
        nixpkgs.follows = "nixos";
        utils.follows = "flake-utils";
      };
    };
    tigger = {
      url = "github:astro/tigger";
      flake = false;
    };
    tracer = {
      # url = "git+https://gitea.nek0.eu/nek0/tracer";
      url = "git+https://gitea.c3d2.de/astro/tracer";
      inputs = {
        affection-src.follows = "affection-src";
        nixpkgs.follows = "nixos";
        flake-utils.follows = "flake-utils";
      };
    };
    treefmt-nix = {
      url = "github:numtide/treefmt-nix";
      inputs.nixpkgs.follows = "nixos";
    };
    yammat = {
      url = "git+https://gitea.c3d2.de/c3d2/yammat.git";
      inputs.nixpkgs.follows = "nixos";
    };
    zentralwerk = {
      url = "git+https://gitea.c3d2.de/zentralwerk/network.git";
      inputs = {
        nixpkgs.follows = "nixos";
        openwrt.follows = "openwrt";
        openwrt-imagebuilder.follows = "openwrt-imagebuilder";
      };
    };
  };

  outputs = inputs@{ self, alert2muc, c3d2-user-module, deployment, harmonia, heliwatch, microvm, nixos, nixos-hardware, nixos-modules, buzzrelay, caveman, oparl-scraper, scrapers, secrets, skyflake, sshlogd, sops-nix, spacemsg, ticker, tigger, yammat, zentralwerk, ... }:
    let
      inherit (nixos) lib;

      inherit (import ./lib/network.nix { inherit lib zentralwerk; }) hostRegistry;

      libC = {
        inherit (import ./lib/nginx.nix {}) defaultListen;
      };

      overlayList = [
        self.overlays
      ];

      ssh-public-keys = import ./ssh-public-keys.nix;

      # Our custom NixOS builder
      nixosSystem' =
        { nixpkgs ? inputs.nixos
        , modules
        , system ? "x86_64-linux"
      }@args:

      { inherit args; } // lib.nixosSystem {
        inherit system;

        modules = [
          (_: {
            _module.args = {
              inherit hostRegistry libC nixos ssh-public-keys zentralwerk;
            };

            nixpkgs.overlays = overlayList;
          })

          self.nixosModules.c3d2
          c3d2-user-module.nixosModule
          nixos-modules.nixosModule
          ./config
          ./modules/pi-sensors.nix
        ] ++ modules;
      };
    in {
      overlays = import ./overlays {
        inherit (inputs) bevy-julia bevy-mandelbrot tracer;
      };

      legacyPackages = lib.attrsets.mapAttrs (_: pkgs: pkgs.appendOverlays overlayList) nixos.legacyPackages;

      packages = import ./packages.nix { inherit hostRegistry inputs lib microvm self; };

      nixosConfigurations = {
        auth = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/auth
          ];
        };

        bind = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/bind
          ];
        };

        blogs = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/blogs
          ];
        };

        broker = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/broker
          ];
        };

        buzzrelay = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            buzzrelay.nixosModules.default
            ./hosts/buzzrelay
          ];
        };

        c3d2-web = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/c3d2-web
          ];
        };

        caveman = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            caveman.nixosModule
            ./hosts/caveman
          ];
        };

        dacbert = nixosSystem' {
          modules = [
            nixos-hardware.nixosModules.raspberry-pi-4
            self.nixosModules.rpi-netboot
            ./hosts/dacbert
          ];
          system = "aarch64-linux";
        };

        dn42 = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/dn42
            {
              # TODO: migrate to sops
              nixpkgs.overlays = [ secrets.overlays.dn42 ];
            }
          ];
        };

        drone = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/drone
          ];
        };

        freifunk = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/freifunk
            {
              # TODO: migrate to sops
              nixpkgs.overlays = with secrets.overlays; [
                freifunk ospf
              ];
            }
          ];
        };

        ftp = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/ftp
          ];
        };

        gitea = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/gitea
          ];
        };

        glotzbert = nixosSystem' {
          modules = [
            ./hosts/glotzbert
            nixos-hardware.nixosModules.common-cpu-intel
            nixos-hardware.nixosModules.common-pc-ssd
          ];
        };

        gnunet = nixosSystem' {
          modules = [
            self.nixosModules.cluster-options
            self.nixosModules.microvm-defaults
            ./hosts/gnunet
          ];
        };

        grafana = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/grafana
          ];
        };

        hedgedoc = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/hedgedoc
          ];
        };

        home-assistant = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/home-assistant
          ];
        };

        hydra = nixosSystem' {
          modules = [
            self.nixosModules.cluster
            skyflake.nixosModules.default
            harmonia.nixosModules.harmonia
            ./hosts/hydra
            {
              nixpkgs.overlays = [
                (final: prev: with final; {
                  harmonia = harmonia.packages.${system}.harmonia.override {
                    pkgs = prev // { inherit (rustPackages_1_66) rustPlatform; };
                  };
                })
              ];
            }
          ];
        };

        jabber = nixosSystem' {
          modules = [
            {
              # TODO: migrate to sops
              nixpkgs.overlays = with secrets.overlays; [ jabber ];
            }
            self.nixosModules.microvm
            ./hosts/jabber
          ];
        };

        leon = nixosSystem' {
          modules = [
            self.nixosModules.cluster-options
            self.nixosModules.microvm-defaults
            ./hosts/leon
          ];
        };

        leoncloud = nixosSystem' {
          modules = [
            self.nixosModules.cluster-options
            self.nixosModules.microvm-defaults
            ./hosts/leoncloud
          ];
        };

        mailtngbert = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/mailtngbert
          ];
          system = "x86_64-linux";
        };

        matrix = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/matrix
          ];
        };

        mastodon = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/mastodon
          ];
        };

        matemat = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/matemat
            yammat.nixosModule
          ];
        };

        mediawiki = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/mediawiki
          ];
        };

        mobilizon = nixosSystem' {
          # TODO: pending https://github.com/NixOS/nixpkgs/pull/119132
          # cherry-picked by sandro into his 22.11 fork
          # nixpkgs = inputs.nixos-mobilizon;
          modules = [
            self.nixosModules.microvm
            ./hosts/mobilizon
          ];
        };

        mucbot = nixosSystem' {
          modules = [
            "${tigger}/module.nix"
            {
              _module.args = { inherit tigger; };

              # TODO: migrate to sops
              nixpkgs.overlays = [ secrets.overlays.mucbot ];
            }
            ./hosts/mucbot
            self.nixosModules.cluster-options
            self.nixosModules.microvm-defaults
          ];
        };

        network-homepage = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/network-homepage
          ];
        };

        nfsroot = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/nfsroot
            {
              _module.args.tftproots = nixos.lib.filterAttrs (name: _:
                builtins.match ".+-tftproot" name != null
              ) self.packages.x86_64-linux;
            }
          ];
        };

        nncp = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            self.nixosModules.nncp
            ./hosts/nncp
          ];
        };

        oparl = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/oparl
            {
              _module.args = { inherit oparl-scraper; };
            }
          ];
        };

        owncast = nixosSystem' {
          modules = [
            self.nixosModules.cluster-options
            self.nixosModules.microvm-defaults
            ./hosts/owncast
          ];
        };

        oxigraph = nixosSystem' {
          modules = [
            self.nixosModules.cluster-options
            self.nixosModules.microvm-defaults
            ./hosts/oxigraph
          ];
        };

        prometheus = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            alert2muc.nixosModules.default
            ./hosts/prometheus
          ];
        };

        pulsebert = nixosSystem' {
          modules = [
            ./hosts/pulsebert
            # build: outputs.nixosConfigurations.pulsebert.config.system.build.sdImage
            # run: unzstd -cd result/sd-image/nixos-sd-image-*-aarch64-linux.img.zst | pv -br | sudo dd bs=4M of=/dev/sdX
            "${inputs.nixos}/nixos/modules/installer/sd-card/sd-image-aarch64-new-kernel.nix"
            {
              nixpkgs = {
                hostPlatform = "aarch64-linux";
                # buildPlatform = "x86_64-linux";
              };
            }
          ];
        };

        public-access-proxy = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/public-access-proxy
          ];
        };

        radiobert = nixosSystem' {
          modules = [
            ./hosts/radiobert
          ];
          system = "aarch64-linux";
        };

        riscbert = nixosSystem' {
          modules = [
            nixos-hardware.nixosModules.starfive-visionfive-v1
            ./hosts/riscbert
            {
              nixpkgs.crossSystem = {
                config = "riscv64-unknown-linux-gnu";
                system = "riscv64-linux";
              };
            }
          ];
          system = "x86_64-linux";
        };

        rpi-netboot = nixosSystem' {
          modules = [
            nixos-hardware.nixosModules.raspberry-pi-4
            self.nixosModules.rpi-netboot
            ./hosts/rpi-netboot
          ];
          system = "aarch64-linux";
        };

        schalter = nixosSystem' {
          modules = [
            "${nixos}/nixos/modules/installer/sd-card/sd-image-raspberrypi.nix"
            ./hosts/schalter
          ];
          system = "x86_64-linux";
        };

        scrape = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ./hosts/scrape
            {
              _module.args = { inherit scrapers; };

              # TODO: migrate to sops
              nixpkgs.overlays = [ secrets.overlays.scrape ];
            }
          ];
        };

        sdrweb = nixosSystem' {
          modules = [
            self.nixosModules.cluster-options
            self.nixosModules.microvm-defaults
            {
              # TODO: migrate to sops
              nixpkgs.overlays = [ secrets.overlays.mucbot ];
            }
            heliwatch.nixosModules.heliwatch
            ./hosts/sdrweb
          ];
        };

        server7 = nixosSystem' {
          modules = [
            ./hosts/server7
            self.nixosModules.cluster-network
            self.nixosModules.cluster
            skyflake.nixosModules.default
            { _module.args = { inherit self; }; }
          ];
        };

        server8 = nixosSystem' {
          modules = [
            ./hosts/server8
            self.nixosModules.cluster-network
            self.nixosModules.cluster
            skyflake.nixosModules.default
            { _module.args = { inherit self; }; }
          ];
        };

        server9 = nixosSystem' {
          modules = [
            ./hosts/server9
            microvm.nixosModules.host
            self.nixosModules.microvm-host
            self.nixosModules.cluster-network
            self.nixosModules.cluster
            skyflake.nixosModules.default
            { _module.args = { inherit self; }; }
          ];
        };

        server10 = nixosSystem' {
          modules = [
            ./hosts/server10
            microvm.nixosModules.host
            self.nixosModules.microvm-host
            self.nixosModules.cluster-network
            self.nixosModules.cluster
            skyflake.nixosModules.default
            { _module.args = { inherit self; }; }
          ];
        };

        spaceapi = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            "${spacemsg}/spaceapi/module.nix"
            ./hosts/spaceapi
          ];
        };

        sshlog = nixosSystem' {
          modules = [
            self.nixosModules.cluster-options
            self.nixosModules.microvm-defaults
            sshlogd.nixosModule
            ./hosts/sshlog
          ];
        };

        stream = nixosSystem' {
          modules = [
            self.nixosModules.cluster-options
            self.nixosModules.microvm-defaults
            ./hosts/stream
          ];
        };

        ticker = nixosSystem' {
          modules = [
            self.nixosModules.microvm
            ticker.nixosModules.ticker
            ./hosts/ticker
          ];
        };

        tmppleroma = nixosSystem' {
          modules = [
            self.nixosModules.cluster-options
            self.nixosModules.microvm-defaults
            sshlogd.nixosModule
            ./hosts/tmppleroma
          ];
        };
      };

      nixosModules = {
        c3d2 = {
          imports = [
            sops-nix.nixosModules.default
            ./modules/audio-server.nix
            ./modules/autoupdate.nix
            ./modules/backup.nix
            ./modules/c3d2.nix
            ./modules/nncp.nix
            ./modules/plume.nix
            ./modules/stats.nix
          ];
          c3d2.nncp.neigh = import ./modules/nncp-relays.nix;
        };
        cluster = ./modules/cluster;
        cluster-options = deployment.nixosModules.deployment-options;
        cluster-network = ./modules/cluster/network.nix;
        microvm-defaults = ./modules/microvm-defaults.nix;
        microvm.imports = [
          microvm.nixosModules.microvm
          ./modules/microvm.nix
        ];
        microvm-host = ./modules/microvm-host.nix;
        nncp = ./modules/nncp.nix;
        rpi-netboot = ./modules/rpi-netboot.nix;
      };

      # `nix develop`
      devShell = lib.mapAttrs (system: sopsPkgs:
        with nixos.legacyPackages.${system};
        mkShell {
          sopsPGPKeyDirs = [ "./keys" ];
          nativeBuildInputs = [
            sopsPkgs.sops-import-keys-hook
          ];
        }
      ) sops-nix.packages;

      hydraJobs =
        lib.mapAttrs (_: nixos.lib.hydraJob) (
          let
            getBuildEntryPoint = _: nixosSystem:
              let
                cfg = nixosSystem.config.microvm.declaredRunner or nixosSystem.config.system.build.toplevel;
              in
              if nixosSystem.config.nixpkgs.system == "aarch64-linux" then
                # increase timeout for chromium
                lib.recursiveUpdate cfg { meta.timeout = 24 * 60 * 60; }
              else
                cfg;
          in
          lib.mapAttrs getBuildEntryPoint self.nixosConfigurations
          # // lib.mapAttrs' (hostname: nixosSystem: lib.nameValuePair
          #   # job display name
          #   (hostname + "-nox")
          #   (getBuildEntryPoint null (nixosSystem' (nixosSystem.args // (with nixosSystem.args; {
          #     modules = modules ++ [
          #       {
          #         # simd.enable = lib.mkForce true;
          #         environment.noXlibs = true;
          #       }
          #     ];
          #     # nixpkgs = nixos-unstable-simd;
          #   }))))
          # ) self.nixosConfigurations
          // nixos.lib.filterAttrs (name: attr:
            (builtins.match ".+-tftproot" name != null && lib.isDerivation attr)
          ) self.packages.aarch64-linux
        );
    };
}
