{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

    flake-utils.url = "github:numtide/flake-utils";

    hardware.url = "github:NixOS/nixos-hardware/master";

    agenix = {
      url = "github:ryantm/agenix";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    home-manager = {
      url = "github:nix-community/home-manager";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    wired = {
      url = "github:Toqozz/wired-notify";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    # nix lsp
    nil = {
      url = "github:oxalica/nil";
      inputs.nixpkgs.follows = "nixpkgs";
      inputs.flake-utils.follows = "flake-utils";
    };

    # my neovim configuration
    myneovim = {
      url = "git+https://git.pleshevski.ru/mynix/neovim";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    # tool to change .env faster
    vnetod = {
      url = "git+https://git.pleshevski.ru/pleshevskiy/vnetod";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    mailserver = {
      url = "gitlab:simple-nixos-mailserver/nixos-mailserver";
      inputs.nixpkgs.follows = "nixpkgs";
      inputs.utils.follows = "flake-utils";
    };
  };

  outputs = inputs @ { self, flake-utils, nixpkgs, hardware, ... }:
    flake-utils.lib.eachDefaultSystem
      (system:
        let
          pkgs = import nixpkgs { inherit system; };
          inherit (pkgs) lib;

          nixos-rebuild = pkgs.nixos-rebuild.override { nix = pkgs.nixVersions.stable; };

          localMachines = lib.filterAttrs (h: m: m.config.deployment.targetHost == null) self.nixosConfigurations;
          vpsMachines = lib.filterAttrs (h: m: m.config.deployment.targetHost != null) self.nixosConfigurations;
        in
        {
          apps = lib.mapAttrs
            (name: program: { type = "app"; program = toString program; })
            (flake-utils.lib.flattenTree {
              deploy = lib.recurseIntoAttrs (lib.mapAttrs
                (hostname: machine: pkgs.writeShellScript "deploy-${hostname}" ''
                  ${nixos-rebuild}/bin/nixos-rebuild switch \
                    --flake .#${hostname} \
                    --target-host ${machine.config.deployment.targetHost} 
                '')
                vpsMachines);

              switch = lib.recurseIntoAttrs (lib.mapAttrs
                (hostname: machine: pkgs.writeShellScript "switch-${hostname}" ''
                  ${nixos-rebuild}/bin/nixos-rebuild switch --flake .#${hostname}
                '')
                localMachines);

              test = lib.recurseIntoAttrs (lib.mapAttrs
                (hostname: machine: pkgs.writeShellScript "test-${hostname}" ''
                  ${nixos-rebuild}/bin/nixos-rebuild test --flake .#${hostname}
                '')
                localMachines);
            });

          devShells = {
            default = pkgs.mkShell {
              packages = with pkgs; [
                stylua # lua formatter
                ormolu # haskell formatter
                inputs.agenix.packages.${system}.agenix
              ];

              # Path to the agenix configuration file
              RULES = "./.agenix_config.nix";
            };
            tools = pkgs.mkShell {
              packages = [ pkgs.gucharmap ];
            };
          };
        })
    // {
      nixosConfigurations =
        nixpkgs.lib.mapAttrs
          (hostname: { system
                     , specialArgs ? { }
                     , extraModules ? [ ]
                     , extraHomeModule ? null
                     , targetHost ? null
                     , nixpkgs ? inputs.nixpkgs
                     }:
            nixpkgs.lib.nixosSystem {
              inherit system;

              specialArgs = { inherit inputs extraHomeModule; } // specialArgs;

              modules =
                (with inputs; [
                  agenix.nixosModule
                  home-manager.nixosModule
                ])
                ++ [ ./machines/${hostname} ]
                ++ extraModules
                ++ [
                  # deployment settings
                  ({ lib, ... }: {
                    options.deployment = with lib; {
                      targetHost = mkOption {
                        type = types.nullOr types.str;
                        readOnly = true;
                        internal = true;
                      };
                    };
                    config.deployment = { inherit targetHost; };
                  })
                  # base home manager settings
                  ({ ... }: {
                    home-manager.useGlobalPkgs = true;
                    home-manager.useUserPackages = true;
                  })
                ];
            })
          (import ./machines inputs);
    };
}
