# NixOS Module for Port Management # # This module provides centralized port management across your NixOS systems. # Define ports once and use them consistently across all services, with # support for host-specific overrides. # # Usage in your NixOS configuration: # # # In your flake.nix or configuration.nix: # imports = [ inputs.m3ta-nixpkgs.nixosModules.default ]; # # m3ta.ports = { # enable = true; # # # Define your default ports # definitions = { # nginx = 80; # grafana = 3000; # prometheus = 9090; # homepage = 8080; # ssh = 22; # }; # # # Define host-specific overrides # hostOverrides = { # laptop = { # nginx = 8080; # Use non-privileged port on laptop # ssh = 2222; # }; # server = { # homepage = 3001; # }; # }; # # # Optionally set the current hostname for automatic port resolution # currentHost = config.networking.hostName; # }; # # # Use ports in your configuration: # services.nginx.defaultHTTPListenPort = config.m3ta.ports.get "nginx"; # services.grafana.settings.server.http_port = config.m3ta.ports.get "grafana"; # # # Or access all ports for the current host: # environment.etc."my-ports.json".text = builtins.toJSON config.m3ta.ports.all; { config, lib, pkgs, ... }: with lib; let cfg = config.m3ta.ports; # Import the ports library portsLib = import ../../lib/ports.nix {inherit lib;}; # Create port helpers from the configuration portHelpers = if cfg.enable then portsLib.mkPortHelpers { ports = cfg.definitions; hostPorts = cfg.hostOverrides; } else null; in { options.m3ta.ports = { enable = mkEnableOption "centralized port management"; definitions = mkOption { type = types.attrsOf types.port; default = {}; example = literalExpression '' { nginx = 80; grafana = 3000; prometheus = 9090; ssh = 22; } ''; description = '' Default port definitions for services. These ports will be used unless overridden by host-specific settings. ''; }; hostOverrides = mkOption { type = types.attrsOf (types.attrsOf types.port); default = {}; example = literalExpression '' { laptop = { nginx = 8080; ssh = 2222; }; server = { nginx = 443; }; } ''; description = '' Host-specific port overrides. Keys are hostnames, values are attribute sets of service-name to port mappings. ''; }; currentHost = mkOption { type = types.nullOr types.str; default = config.networking.hostName; defaultText = literalExpression "config.networking.hostName"; example = "laptop"; description = '' The current hostname to use for port resolution. Defaults to the system's hostname. Set to null to disable host-specific overrides. ''; }; # Computed option - provides access to port helpers get = mkOption { type = types.functionTo types.port; readOnly = true; internal = true; description = '' Function to get a port for a service. Automatically uses the current host for overrides. ''; }; getForHost = mkOption { type = types.functionTo (types.functionTo types.port); readOnly = true; internal = true; description = '' Function to get a port for a service on a specific host. Usage: config.m3ta.ports.getForHost "hostname" "service" ''; }; all = mkOption { type = types.attrsOf types.port; readOnly = true; internal = true; description = '' All ports for the current host (defaults merged with overrides). ''; }; allForHost = mkOption { type = types.functionTo (types.attrsOf types.port); readOnly = true; internal = true; description = '' Function to get all ports for a specific host. Usage: config.m3ta.ports.allForHost "hostname" ''; }; services = mkOption { type = types.listOf types.str; readOnly = true; internal = true; description = '' List of all defined service names. ''; }; }; config = mkIf cfg.enable { assertions = [ { assertion = cfg.definitions != {}; message = "m3ta.ports.definitions must not be empty when m3ta.ports.enable is true"; } ]; m3ta.ports = { # Function to get port for current host get = service: assert assertMsg (portHelpers != null) "Port helpers not initialized"; assert assertMsg (cfg.definitions ? ${service}) "Service '${service}' not defined in m3ta.ports.definitions"; portHelpers.getPort service cfg.currentHost; # Function to get port for specific host getForHost = host: service: assert assertMsg (portHelpers != null) "Port helpers not initialized"; assert assertMsg (cfg.definitions ? ${service}) "Service '${service}' not defined in m3ta.ports.definitions"; portHelpers.getPort service host; # All ports for current host all = if portHelpers != null then portHelpers.getHostPorts cfg.currentHost else {}; # Function to get all ports for specific host allForHost = host: assert assertMsg (portHelpers != null) "Port helpers not initialized"; portHelpers.getHostPorts host; # List all services services = if portHelpers != null then portHelpers.listServices else []; }; # Optional: Create a JSON file with all ports for easy inspection environment.etc."m3ta/ports.json" = mkIf cfg.enable { text = builtins.toJSON { hostname = cfg.currentHost; ports = cfg.all; allDefinitions = cfg.definitions; hostOverrides = cfg.hostOverrides; }; mode = "0444"; }; }; }