From c95e317aeb604e01de1cde464fecb7cfa7df0ed0 Mon Sep 17 00:00:00 2001 From: Adriano Sela Aviles Date: Sun, 15 Mar 2026 10:07:09 -0700 Subject: [PATCH] ipn/localapi: expose local node service details over api --- ipn/localapi/localapi.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ipn/localapi/localapi.go b/ipn/localapi/localapi.go index 5eec66e64..bc9b47642 100644 --- a/ipn/localapi/localapi.go +++ b/ipn/localapi/localapi.go @@ -85,6 +85,7 @@ var handler = map[string]LocalAPIHandler{ "set-expiry-sooner": (*Handler).serveSetExpirySooner, "shutdown": (*Handler).serveShutdown, "start": (*Handler).serveStart, + "service-details": (*Handler).serveServiceDetails, "status": (*Handler).serveStatus, "whois": (*Handler).serveWhoIs, } @@ -1728,3 +1729,23 @@ func (h *Handler) serveGetAppcRouteInfo(w http.ResponseWriter, r *http.Request) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(res) } + +// serveServiceDetails handles GET /localapi/v0/service-details and returns the +// list of VIP services that the control plane has approved this node to serve, +// including their names, assigned IP addresses, ports, and annotations (e.g. +// proxy service configuration). Returns null if no service details are present. +func (h *Handler) serveServiceDetails(w http.ResponseWriter, r *http.Request) { + if !h.PermitRead { + http.Error(w, "service-details access denied", http.StatusForbidden) + return + } + if r.Method != httpm.GET { + http.Error(w, "only GET allowed", http.StatusMethodNotAllowed) + return + } + details := h.b.NetMap().GetVIPServiceDetails() + w.Header().Set("Content-Type", "application/json") + e := json.NewEncoder(w) + e.SetIndent("", "\t") + e.Encode(details) +}