feat(portal-tunnel): add optional --metrics-addr flag

cognitive committed Apr 30, 2026 at 05:19 UTC 6eb0144b97627d6d188757b690da6fde6dafc5a8
1 file changed +20
cmd/portal-tunnel/main.go
+20
@@ -6,12 +6,14 @@ import (
6 "flag"
7 "fmt"
8 "io"
9 + "net/http"
10 "os"
11 "strings"
12 "sync"
13 "text/tabwriter"
14 "time"
15
16 + "github.com/prometheus/client_golang/prometheus/promhttp"
17 "github.com/rs/zerolog"
18 "github.com/rs/zerolog/log"
19
@@ -62,6 +64,7 @@ type exposeFlags struct {
64 tcp bool
65 maxActiveRelays int
66 multiHopDepth int
67 + metricsAddr string
68 }
69
70 func runExposeCommand(args []string) error {
@@ -88,6 +91,7 @@ func runExposeCommand(args []string) error {
91 utils.BoolFlagEnv(fs, &flags.tcp, "tcp", false, "Request a dedicated TCP port on the relay for raw TCP services (no TLS; e.g., Minecraft, game servers)", "TCP_ENABLED")
92 utils.IntFlagEnv(fs, &flags.maxActiveRelays, "max-active-relays", 3, nil, "Maximum number of auto-selected relays to keep connected; explicit --relays are always included", "MAX_ACTIVE_RELAYS")
93 utils.IntFlagEnv(fs, &flags.multiHopDepth, "multi-hop-depth", 0, nil, "Automatically select one multi-hop route with this hop count; 0 or 1 disables multi-hop", "MULTI_HOP_DEPTH")
94 + utils.StringFlag(fs, &flags.metricsAddr, "metrics-addr", "", "Optional address (host:port) to serve Prometheus /metrics. Empty = disabled.")
95
96 if err := utils.ParseFlagSet(fs, args, printExposeUsage); err != nil {
97 if errors.Is(err, flag.ErrHelp) {
@@ -117,6 +121,22 @@ func runExposeCommand(args []string) error {
121 ctx, stop := utils.SignalContext()
122 defer stop()
123
124 + if flags.metricsAddr != "" {
125 + mux := http.NewServeMux()
126 + mux.Handle("/metrics", promhttp.Handler())
127 + srv := &http.Server{
128 + Addr: flags.metricsAddr,
129 + Handler: mux,
130 + ReadHeaderTimeout: 5 * time.Second,
131 + }
132 + go func() {
133 + log.Info().Str("metrics_addr", flags.metricsAddr).Msg("metrics server listening")
134 + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
135 + log.Error().Err(err).Msg("metrics server error")
136 + }
137 + }()
138 + }
139 +
140 exposure, err := sdk.Expose(ctx, sdk.ExposeConfig{
141 RelayURLs: utils.SplitCSV(flags.relayCSV),
142 Discovery: flags.discovery,