{
  inputs = {
    flake-utils.url = "github:numtide/flake-utils";
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
    nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    hardware.url = "github:NixOS/nixos-hardware/master";
    impermanence.url = "github:nix-community/impermanence";
    disko = {
      url = "github:nix-community/disko";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    firefox-addons.url = "github:nix-community/nur-combined/master?dir=repos/rycee/pkgs/firefox-addons";

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

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

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

    lan-mouse = {
      url = "github:feschber/lan-mouse";
      inputs.nixpkgs.follows = "nixpkgs-unstable";
    };

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

    # my neovim configuration
    nixeovim = {
      url = "git+https://git.pleshevski.ru/pleshevskiy/nixeovim";
      inputs.nixpkgs.follows = "nixpkgs-unstable";
      inputs.flake-utils.follows = "flake-utils";
    };
  };

  outputs = { self, flake-utils, nixpkgs, nixeovim, ... } @ inputs:
    let inherit (flake-utils.lib) eachSystem system; in
    eachSystem [ system.x86_64-linux ]
      (system:
        let
          pkgs = import nixpkgs { inherit system; };
          inherit (pkgs) lib nixos-rebuild;

          nixeovimPackage = config: nixeovim.lib.mkNixeovimPackage { inherit system config; };

          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
        {
          packages = {
            neovim-dev = nixeovimPackage ./neovim/dev.nix;
          };

          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 root@${machine.config.deployment.targetHost} \
                    $@
                '')
                vpsMachines);

              switch = lib.recurseIntoAttrs (lib.mapAttrs
                (hostname: machine: pkgs.writeShellScript "switch/${hostname}" ''
                  set -e
                  ${nixos-rebuild}/bin/nixos-rebuild switch --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";
            };
            disk = pkgs.mkShell {
              packages = [
                inputs.disko.packages.${system}.disko
              ];
            };
            tools = pkgs.mkShell {
              packages = with pkgs; [
                mkpasswd
                gucharmap
                wireguard-tools
              ];
            };
          };
        })
    // {
      nixosConfigurations =
        nixpkgs.lib.mapAttrs
          (hostname: { system
                     , specialArgs ? { }
                     , extraModules ? [ ]
                     , targetHost ? null
                     , nixpkgs ? inputs.nixpkgs
                     }:
            nixpkgs.lib.nixosSystem {
              inherit system;

              specialArgs = {
                inherit inputs;
                globalData = import ./data.nix;
                usersPath = ./users;
                hostsPath = ./hosts;
                packagesPath = ./packages;
              } // specialArgs;

              modules =
                (with inputs; [
                  agenix.nixosModules.default
                  home-manager.nixosModules.default
                  disko.nixosModules.disko
                  impermanence.nixosModules.impermanence
                ])
                ++ [
                  # 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.backupFileExtension = "backup";
                    home-manager.useGlobalPkgs = true;
                    home-manager.useUserPackages = true;
                    home-manager.extraSpecialArgs = {
                      packagesPath = ./packages;
                      hostsPath = ./hosts;
                    };
                    home-manager.sharedModules = [
                      {
                        imports = [
                          ./modules/home-manager
                          inputs.wired.homeManagerModules.default
                          inputs.lan-mouse.homeManagerModules.default
                        ];
                      }
                    ];
                  })
                ]
                ++ extraModules
                ++ [ ./modules/nixos ]
                ++ [ ./hosts/${hostname}/configuration.nix ];
            })
          (import ./hosts inputs);
      diskoConfigurations = {
        asus-gl553vd = import ./hosts/asus-gl553vd/disk-config.nix;
      };
    };
}