let _concerns_lib = import "schemas/lib/concerns.ncl" in { Wireguard = { name | String | default = "wireguard", version | String, image | String, namespace | String | default = "vpn", mode | [| 'cluster |] | default = 'cluster, node | String, fip_name | String, lb_ipam_ip | String | default = "", vpn_subnet | String, server_ip | String, wg_default_address | String | std.contract.from_predicate (fun s => std.string.is_match "^[0-9]+\\.[0-9]+\\.[0-9]+\\.x$" s) | doc m%" Client IP pattern with literal `x` placeholder that wg-easy replaces per new peer (e.g., "10.200.0.x" → peers get 10.200.0.2, 10.200.0.3, ...). Format enforced by predicate: must match `^[0-9]+\.[0-9]+\.[0-9]+\.x$`. The first three octets should align with vpn_subnet (soft constraint — Nickel cannot cross-reference sibling fields in a single-field contract; misalignment manifests as peers unable to receive routable IPs). "% | default = "10.200.0.x", listen_port | Number | default = 51820, ui_port | Number | default = 51821, dns | Array String | default = ["1.1.1.1", "8.8.8.8"], mtu | Number | default = 1420, keepalive | Number | default = 25, host_network | Bool | doc m%" Run the wireguard Pod with spec.hostNetwork: true, bypassing Cilium CNI. When true: the deployment template emits hostNetwork=true; the lb-ipam-pool and service-vpn templates render empty (no CiliumLBIPPool nor LoadBalancer Service is created). FIP binding moves to fip_controller (declared separately in infra). lb_ipam_ip remains the advertised WG_HOST endpoint in the configmap-env. "% | default = false, client_allowed_ips | Array String | default = [], wg_post_up_extra | Array String | doc m%" Extra iptables/nft commands appended to wg-easy's default WG_POST_UP. Use this to add MASQUERADE/forward rules for paths the default (`-o eth0`) doesn't cover — most commonly the private/wuwei interface when running with host_network=true (e.g., on Hetzner enp7s0). The strings are joined with `;` and passed via WG_POST_UP env. wg-easy interpolates `%i` as the WG interface name. "% | default = [], wg_post_down_extra | Array String | doc m%" Mirror of wg_post_up_extra for tear-down. Each entry should `iptables -D ...` an entry from wg_post_up_extra so the rule set is balanced. Passed via WG_POST_DOWN. "% | default = [], kubeconfig | String | optional, requires | { storage | { size | String, persistent | Bool } | optional, storage_class | String | optional, ports | Array { port | Number, protocol | String | default = "UDP", exposure | [| 'public, 'private, 'internal |] | default = 'public, } | default = [], credentials | Array String | default = [], } | default = {}, provides | { service | String | optional, ports | Array Number | default = [], } | default = {}, operations | { install | Bool | default = true, update | Bool | default = false, delete | Bool | default = false, restart | Bool | default = false, health | Bool | default = false, } | default = {}, # ServiceConcerns umbrella (ADR-008). VPN gateway — config in git, peers stateless. concerns | _concerns_lib.ServiceConcerns | optional, .. }, }