{ lib, config, ... }: let state = builtins.import ../state.nix; ty = { ipv4 = lib.types.strMatching "[0-9]{1,3}(\\.[0-9]{1,3}){3}"; }; in { options.state = with lib; { vpn.ipv4 = mkOption { type = ty.ipv4; description = "VPN IPv4"; readOnly = true; }; vpn.ipv6 = mkOption { type = types.str; description = "VPN IPv6"; readOnly = true; }; vpn.machine = mkOption { type = with types; attrsOf (submodule { options = { ipv4 = mkOption { type = ty.ipv4; description = "VPN IPv4"; }; ipv6 = mkOption { type = str; description = "VPN IPv6"; }; }; }); description = "VPN IP definitions for all available machines"; readOnly = true; }; vpn.dns = mkOption { type = ty.ipv4; description = "VPN internal DNS server address"; readOnly = true; }; services = mkOption { type = with types; attrsOf (submodule { options = { host = mkOption { type = str; description = "Host where the service runs"; }; port = mkOption { type = number; description = "Port over which the service can be accessed"; }; external = mkOption { type = nullOr bool; description = "Whether this service should be exposed (outside of VPN)"; }; }; }); description = "Service settings"; readOnly = true; }; }; config.state = let host = config.networking.hostName; in { vpn.ipv4 = if state ? vpn.${host}.v4 then state.vpn.${host}.v4 else builtins.throw "VPN IPv4 not defined for ${host}"; vpn.ipv6 = if state ? vpn.${host}.v6 then state.vpn.${host}.v6 else builtins.throw "VPN IPv6 not defined for ${host}"; vpn.machine = if state ? vpn then builtins.mapAttrs (machine: conf: { ipv4 = conf.v4; ipv6 = conf.v6; }) state.vpn else builtins.throw "No VPN definitions"; services = if state ? services then state.services else {}; }; }