{
  lib,
  config,
  ...
}: let
  dataDir = "/var/lib/acme";
  httpPort = 80;
  httpsPort = 443;
in {
  options.host.reverse_proxy = {
    enable = lib.mkEnableOption "turn on the reverse proxy";
    hostname = lib.mkOption {
      type = lib.types.str;
      description = "what host name are we going to be proxying from";
    };
    forceSSL = lib.mkOption {
      type = lib.types.bool;
      description = "force connections to use https";
      default = config.host.reverse_proxy.enableACME;
    };
    enableACME = lib.mkOption {
      type = lib.types.bool;
      description = "auto renew certificates";
      default = true;
    };
    subdomains = lib.mkOption {
      type = lib.types.attrsOf (lib.types.submodule ({name, ...}: {
        options = {
          subdomain = lib.mkOption {
            type = lib.types.str;
            description = "what is the default subdomain to be used for this application to be used for";
            default = name;
          };
          extraSubdomains = lib.mkOption {
            type = lib.types.listOf lib.types.str;
            description = "extra domains that should be configured for this domain";
            default = [];
          };

          target = lib.mkOption {
            type = lib.types.str;
            description = "what url will all traffic to this application be forwarded to";
          };

          websockets.enable = lib.mkEnableOption "should the default config proxy websockets";

          forwardHeaders.enable = lib.mkEnableOption "should the default config contain forward headers";

          extraConfig = lib.mkOption {
            type = lib.types.lines;
            default = "";
            description = ''
              These lines go to the end of the upstream verbatim.
            '';
          };
        };
      }));
    };
  };

  config = lib.mkIf config.host.reverse_proxy.enable (lib.mkMerge [
    {
      security.acme = lib.mkIf config.host.reverse_proxy.enableACME {
        acceptTerms = true;
        defaults.email = "jan-leila@protonmail.com";
      };

      services.nginx = {
        enable = true;
        virtualHosts = lib.mkMerge (
          lib.lists.flatten (
            lib.attrsets.mapAttrsToList (
              name: value: let
                hostConfig = {
                  forceSSL = config.host.reverse_proxy.forceSSL;
                  enableACME = config.host.reverse_proxy.enableACME;
                  locations = {
                    "/" = {
                      proxyPass = value.target;
                      proxyWebsockets = value.websockets.enable;
                      recommendedProxySettings = value.forwardHeaders.enable;
                      extraConfig =
                        value.extraConfig;
                    };
                  };
                };
              in (
                [
                  {
                    ${"${value.subdomain}.${config.host.reverse_proxy.hostname}"} = hostConfig;
                  }
                ]
                ++ builtins.map (subdomain: {${"${subdomain}.${config.host.reverse_proxy.hostname}"} = hostConfig;})
                value.extraSubdomains
              )
            )
            config.host.reverse_proxy.subdomains
          )
        );
      };

      networking.firewall.allowedTCPPorts = [
        httpPort
        httpsPort
      ];
    }
    (lib.mkIf config.host.impermanence.enable {
      # TODO: figure out how to write an assertion for this
      # assertions = [
      #   {
      #     assertion = security.acme.certs.<name>.directory == dataDir;
      #     message = "postgres data directory does not match persistence";
      #   }
      # ];
      environment.persistence."/persist/system/root" = {
        enable = true;
        hideMounts = true;
        directories = [
          {
            directory = dataDir;
            user = "acme";
            group = "acme";
          }
        ];
      };
    })
  ]);
}