let lib = import ; net = import ../config/network.nix; end = import ../config/endpoints.nix; endpointValidation = import ../validation/endpoints.nix; autoSshValidation = import ../validation/auto_ssh.nix; endpoints = endpointValidation.validateEndpointsShape end; devices = autoSshValidation.getDevices net; autoSshDevices = autoSshValidation.getAutoSshDevices devices; matchesDomain = endpointDomain: remoteDomain: if builtins.isString endpointDomain && builtins.isString remoteDomain then endpointDomain == remoteDomain || lib.hasSuffix ".${remoteDomain}" endpointDomain else throw "Endpoint and remote domains must be strings."; getRemotePortMap = device: autoSshValidation.getRemotePortMap device; getPortOffset = device: autoSshValidation.getPortOffset device; ensurePortRange = context: port: if builtins.isInt port && port >= 1 && port <= 65535 then port else throw "${context} must be in range 1..65535."; resolveRemotePort = remotePortMap: portOffset: endpoint: let localPort = if endpoint ? port then ensurePortRange "Endpoint port '${toString endpoint.port}'" endpoint.port else throw "Endpoint is missing required field: port."; overrides = lib.filter (entry: entry.localPort == localPort) remotePortMap; in if overrides != [] then ensurePortRange "remotePortMap remotePort '${toString (builtins.head overrides).remotePort}'" (builtins.head overrides).remotePort else ensurePortRange "Computed remote port (local ${toString localPort} + offset ${toString portOffset})" (localPort + portOffset); mapEndpointToForward = remotePortMap: portOffset: endpoint: { remote = resolveRemotePort remotePortMap portOffset endpoint; localAddress = "localhost"; localPort = endpoint.port; }; endpointForHttpRedirect = endpoint: endpoint // { port = 80; }; portsByRemote = lib.mapAttrs (_: device: let remoteDomain = if device ? domain then device.domain else throw "Auto SSH device is missing required field: domain."; remotePortMap = getRemotePortMap device; portOffset = getPortOffset device; matchedEndpoints = lib.filter (endpoint: if endpoint.force_ssl && endpoint.port == 80 then throw "Endpoint '${endpoint.domain}${endpoint.endpoint}' cannot use force_ssl=true on port 80." else if endpoint.type == "proxy" && endpoint.content.type != "service" then throw "Proxy endpoint '${endpoint.domain}${endpoint.endpoint}' must use content.type = 'service'." else matchesDomain endpoint.domain remoteDomain ) endpoints; forwards = lib.concatMap (endpoint: let baseForward = mapEndpointToForward remotePortMap portOffset endpoint; httpRedirectForward = mapEndpointToForward remotePortMap portOffset (endpointForHttpRedirect endpoint); in if endpoint.force_ssl then [ baseForward httpRedirectForward ] else [ baseForward ] ) matchedEndpoints; in lib.unique forwards ) autoSshDevices; in rec { inherit portsByRemote; }