{
  config,
  lib,
  ...
}:
with lib; let
  inherit (import ./shared.nix) netName interface service;

  ca = ../../keys/ca.crt;

  baseFirewall = {
    outbound = [
      # Allow all outbound traffic
      {
        port = "any";
        proto = "any";
        host = "any";
      }
    ];
    inbound = [
      # Allow pings from anyone
      {
        port = "any";
        proto = "icmp";
        host = "any";
      }
    ];
  };

  baseServer = {
    isLighthouse = true;

    listen = {
      host = "0.0.0.0";
      port = 4242;
    };
  };
  baseClient = let
    lhs = {"10.13.0.1" = ["min.rip:4242"];};
    lhsInternal = attrNames lhs;
  in {
    lighthouses = lhsInternal;
    staticHostMap = lhs;

    settings.punchy = {
      punch = true;
      respond = true;
    };
  };

  cfg = config.gen.nebula;
in {
  options.gen.nebula = {
    enable = mkEnableOption "nebula mesh vpn";
    enableLighthouse = mkEnableOption "lighthouse functionality";

    cert = mkOption {
      type = types.path;
      description = "nebula node cert path";
    };
    key = mkOption {
      type = types.path;
      description = "nebula node key path";
    };

    extraInbound = mkOption {
      type = types.listOf types.attrs;
      description = "extra inbound firewall rules";
    };
  };

  config = mkMerge [
    (mkIf cfg.enable {
      networking.firewall.trustedInterfaces = [interface];

      services.nebula.networks.${netName} = mkMerge [
        {
          inherit ca;
          inherit (cfg) cert key;

          firewall = {
            inherit (baseFirewall) outbound;
            inbound = baseFirewall.inbound ++ cfg.extraInbound;
          };
        }
        (mkIf cfg.enableLighthouse baseServer)
        (mkIf (!cfg.enableLighthouse) baseClient)
      ];
    })
    (mkIf config.services.openssh.enable {
      # Make sure sshd starts after nebula
      # TODO: is this necessary?
      systemd.services.sshd.after = [service];
    })
  ];
}