Documentation
¶
Overview ¶
Package diagnostics serves operational endpoints on a listener that is separate from the application listener.
The reason to separate is enforceability, not middleware coverage. Be precise about what this does and does not buy, because the distinction is easy to overstate:
- It does NOT authenticate, rate limit, or audit the endpoints. This listener carries no middleware at all (see the warning below). An endpoint served here is exactly as unauthenticated as it was on the application mux.
- It DOES make the endpoints governable by port. Kubernetes NetworkPolicy is L3/L4: it selects pods, ports, and protocols, and cannot filter on HTTP path. While /metrics shares the application port, no policy can permit MCP traffic while denying metrics scraping. On its own port, it can. Route-level controls (Gateway API, Ingress path rules) govern only what reaches the gateway, so they address north-south exposure but leave pod-to-pod traffic untouched.
- It DOES keep the endpoints off the port deployments route publicly, so the safe outcome does not depend on every operator getting route rules right.
Serving them on the application mux is what made the port-level control impossible: Go's ServeMux resolves the most specific registered pattern first, so an explicitly registered "/metrics" always beats the "/" catch-all that carries the middleware chain.
This mirrors the ToolHive operator, which already binds its own metrics endpoint to a separate address (--metrics-bind-address), and the wider convention for diagnostics ports: etcd's --listen-metrics-urls and controller-runtime's --metrics-bind-address.
Note that /health deliberately stays on the application listener. Kubernetes liveness and readiness probes target the application port, and the proxy health response carries no sensitive fields.
Anything registered on this listener is served WITHOUT authentication, authorization, rate limiting, or audit — that is the whole point of moving it off the application listener, and it is why the listener must not be routed publicly. Only add read-only, non-sensitive endpoints here. In particular, do not register pprof or any other debug handler that exposes process memory, goroutine state, or configuration.
Index ¶
Constants ¶
const DefaultPort = 9464
DefaultPort is the port the diagnostics listener binds when no port is configured. 9464 is the OpenTelemetry specification's default for the Prometheus exporter (OTEL_EXPORTER_PROMETHEUS_PORT), so scrapers already expect metrics there.
A fixed default rather than an arbitrary one matters for deployments: a scraper needs a predictable target. When the port is already taken — several CLI workloads on one machine, for instance — Start falls back to an available port and logs the resolved address.
const MetricsPath = "/metrics"
MetricsPath is the path the Prometheus metrics handler is served on.
Variables ¶
This section is empty.
Functions ¶
func NotServedHereHandler ¶
NotServedHereHandler answers requests for MetricsPath on an application listener, where metrics are deliberately not served.
It exists so the response explains itself. A bare 404 is indistinguishable from a typo, and the failure is otherwise silent from the server side: an upgrade moves the endpoint and the operator sees only a Prometheus target going down.
The body deliberately names no port. The diagnostics listener honours a configured port and falls back to an available one when that is taken (see Server.bind), so any number written here would be wrong for both of those supported configurations — and a confidently wrong address is worse than none when the whole point is to redirect someone who is already lost. The startup log carries the resolved address and is the one source that is always correct.
The status stays 404 rather than 410 Gone: this handler is also registered on deployments that never served metrics on this port, where claiming the resource was removed would be untrue.
func ResolvePort ¶
ResolvePort returns the diagnostics port to request for a configured value. Zero means "not configured" and selects DefaultPort, so a scraper has a predictable target rather than an arbitrary one. Callers that genuinely want an OS-assigned port pass 0 to New directly instead.
Types ¶
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server serves diagnostics endpoints on a dedicated listener.
The zero value is not usable; construct one with New.
func New ¶
New creates a diagnostics server that serves metricsHandler at MetricsPath.
host is the bind address. port is the requested port; pass DefaultPort unless a deployment needs a different one, since a predictable port is what lets a scraper find the endpoint. Passing 0 asks the OS for an arbitrary available port, which is useful in tests but leaves nothing for a scraper to target. Either way, Start falls back to an available port if the requested one is taken, so the resolved port is only known after Start — read it from Addr or Port.
The port is not bound until Start is called.
func (*Server) Addr ¶
Addr returns the resolved listen address, or an empty string before Start succeeds or after Stop.