{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    nixpkgs-small.url = "github:NixOS/nixpkgs/nixos-unstable-small";
    nixos-hardware.url = "github:NixOS/nixos-hardware";
    nix-index-database.url = "github:Mic92/nix-index-database";
    nix-index-database.inputs.nixpkgs.follows = "nixpkgs";
    darwin.url = "github:lnl7/nix-darwin/master";
    darwin.inputs.nixpkgs.follows = "nixpkgs";
    emacs-overlay.url = "github:nix-community/emacs-overlay";
    home-manager.url = "github:nix-community/home-manager";
    home-manager.inputs.nixpkgs.follows = "nixpkgs";
    utils.url = "github:numtide/flake-utils";
    agenix.url = "github:ryantm/agenix";
    agenix.inputs.nixpkgs.follows = "nixpkgs";
    deploy-rs.url = "github:serokell/deploy-rs";
    searchix.url = "git+https://git.alanpearce.eu/searchix";
    golink = {
      url = "github:tailscale/golink";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs =
    inputs@
    { self
    , utils
    , nixpkgs
    , nixpkgs-small
    , nixos-hardware
    , emacs-overlay
    , home-manager
    , darwin
    , nix-index-database
    , agenix
    , deploy-rs
    , searchix
    , golink
    , ...
    }:
    let
      readOverlays = path:
        let content = builtins.readDir path; in
        map (n: import (path + ("/" + n)))
          (builtins.filter
            (n:
              (builtins.match ".*\\.nix" n != null &&
              # ignore Emacs lock files (.#foo.nix)
              builtins.match "\\.#.*" n == null) ||
              builtins.pathExists (path + ("/" + n + "/default.nix")))
            (builtins.attrNames content));

      mkHomeConfiguration = { modules, system }: home-manager.lib.homeManagerConfiguration {
        pkgs = import nixpkgs {
          inherit system;
          overlays = readOverlays (toString ./overlays) ++ [
            agenix.overlays.default
            emacs-overlay.overlays.default
            (self: super: {
              personal = import ./packages/overlay.nix self super;
              enchant = super.enchant.override {
                withHspell = false;
                withAspell = false;
              };
            })
          ];
        };

        inherit modules;
        extraSpecialArgs = {
          inherit inputs system;
        };
      };
    in
    {
      nixosConfigurations.prefect = nixpkgs.lib.nixosSystem {
        system = utils.lib.system.x86_64-linux;
        specialArgs = { inherit inputs; };
        modules = [
          ./system/prefect.nix
        ] ++ (with nixos-hardware.nixosModules; [
          common-cpu-amd
          common-cpu-amd-pstate
          common-pc-ssd
          common-pc
          common-gpu-nvidia-nonprime
        ]);
      };
      nixosConfigurations.nanopi = nixpkgs-small.lib.nixosSystem {
        system = utils.lib.system.aarch64-linux;
        specialArgs = { inherit inputs; };
        modules = [
          agenix.nixosModules.default
          nixos-hardware.nixosModules.friendlyarm-nanopi-r5s
          ./system/nanopi.nix
        ];
      };
      nixosConfigurations.linde = nixpkgs-small.lib.nixosSystem {
        system = utils.lib.system.aarch64-linux;
        specialArgs = { inherit inputs; };
        modules = [
          agenix.nixosModules.default
          searchix.nixosModules.web
          golink.nixosModules.default
          ./packages/modules/nixos/laminar.nix
          ./system/linde.nix
        ];
      };
      darwinConfigurations.marvin = darwin.lib.darwinSystem {
        system = utils.lib.system.aarch64-darwin;
        specialArgs = { inherit inputs; };
        modules = [
          ./system/marvin.nix
          ./packages/modules/darwin/caddy
        ];
      };
      homeConfigurations."alan@marvin" = mkHomeConfiguration {
        system = utils.lib.system.aarch64-darwin;
        modules = [
          ./user/marvin.nix
          ./private/default.nix
          ./private/ssh.nix
          nix-index-database.hmModules.nix-index
        ];
      };
      homeConfigurations."alan@prefect" = mkHomeConfiguration {
        system = utils.lib.system.x86_64-linux;
        modules = [
          ./user/prefect.nix
          ./private/default.nix
          ./private/ssh.nix
          nix-index-database.hmModules.nix-index
        ];
      };
      homeConfigurations."alan@nanopi" = mkHomeConfiguration {
        system = utils.lib.system.aarch64-linux;
        modules = [
          ./user/nanopi.nix
          ./private/default.nix
          nix-index-database.hmModules.nix-index
        ];
      };
      homeConfigurations."alan@linde" = mkHomeConfiguration {
        system = utils.lib.system.aarch64-linux;
        modules = [
          ./user/server.nix
          ./private/default.nix
          nix-index-database.hmModules.nix-index
        ];
      };

      deploy = {
        nodes.linde = {
          hostname = "linde";
          profiles.system = {
            user = "root";
            sshUser = "root";
            path = deploy-rs.lib.${utils.lib.system.aarch64-linux}.activate.nixos
              self.nixosConfigurations.linde;
          };
          profiles.alan = {
            user = "alan";
            path = deploy-rs.lib.${utils.lib.system.aarch64-linux}.activate.home-manager
              self.homeConfigurations."alan@linde";
          };
        };
      };
    } // utils.lib.eachDefaultSystem (system:
    let
      pkgs = import nixpkgs { inherit system; };
    in
    {
      devShells = {
        default = pkgs.mkShell {
          packages = [
            deploy-rs.packages.${system}.default
            agenix.packages.${system}.default
          ];
        };
      };
    });


  nixConfig = {
    extra-substituters = [
      "https://toyvo.cachix.org"
    ];
    extra-trusted-public-keys = [
      "toyvo.cachix.org-1:s++CG1te6YaS9mjICre0Ybbya2o/S9fZIyDNGiD4UXs="
    ];
  };
}