Skip to content

Commit cdf06ff

Browse files
committed
Runtime maintenance banner endpoint
PUT /api/banner (admin-token-gated, header X-Admin-Token or query param) updates the dashboard's amber notice in-place. Persisted to \$STORE_DIR/banner.txt atomically; survives restart. Both GET and PUT are admin-only (the public still reads the rendered banner via /api/stats.maintenance_banner). Empty-body PUT clears the banner. 8-case test coverage: 401 without token, 401 wrong token, valid PUT round-trip, public /api/stats surfaces banner, persistence file written, empty-body clears, 405 on DELETE.
1 parent 0f5305c commit cdf06ff

3 files changed

Lines changed: 454 additions & 163 deletions

File tree

cmd/rendezvous/main.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import (
88
"log/slog"
99
"os"
1010
"os/signal"
11+
"path/filepath"
12+
"runtime"
1113
"syscall"
14+
"time"
1215

1316
"github.com/TeoSlayer/pilotprotocol/pkg/beacon"
1417
"github.com/TeoSlayer/pilotprotocol/pkg/config"
@@ -33,6 +36,11 @@ func main() {
3336
logFormat := flag.String("log-format", "text", "log format (text, json)")
3437
adminToken := flag.String("admin-token", "", "admin token for network creation (empty = creation disabled)")
3538
dashboardToken := flag.String("dashboard-token", "", "token for per-network stats on dashboard (empty = public-only)")
39+
maintenanceBanner := flag.String("maintenance-banner", "", "free-form notice rendered on the dashboard (empty = no banner)")
40+
replToken := flag.String("repl-token", "", "shared token required for hot-standby replication (empty = replication disabled)")
41+
staleThreshold := flag.Duration("stale-threshold", 30*time.Minute, "how long since last heartbeat before a node is considered stale on the dashboard (e.g. 30m, 5m). Default 30m tolerates spoof reconnect storms; smaller values give faster dashboard reflection of disconnects.")
42+
mutexProfileFraction := flag.Int("mutex-profile-fraction", 1000, "rate for runtime mutex contention profiling (1/N events sampled; 0 = off). Always-on at low overhead so future incidents have profile data ready without scrambling.")
43+
blockProfileRate := flag.Int("block-profile-rate", 10000, "rate for runtime blocking profile in nanoseconds (0 = off). Captures goroutines blocked on chan/select/Cond — same rationale as -mutex-profile-fraction.")
3644
flag.Parse()
3745

3846
if *configPath != "" {
@@ -45,6 +53,16 @@ func main() {
4553

4654
logging.Setup(*logLevel, *logFormat)
4755

56+
// Enable mutex + block profiling early so contention from startup paths
57+
// is captured. Net runtime cost at the chosen rates is ~1% CPU; the
58+
// rendezvous endpoint exposes /debug/pprof/{mutex,block} via DefaultServeMux.
59+
if *mutexProfileFraction > 0 {
60+
runtime.SetMutexProfileFraction(*mutexProfileFraction)
61+
}
62+
if *blockProfileRate > 0 {
63+
runtime.SetBlockProfileRate(*blockProfileRate)
64+
}
65+
4866
slog.Info("starting rendezvous server", "version", version)
4967

5068
// Start beacon
@@ -57,13 +75,29 @@ func main() {
5775

5876
// Start registry
5977
r := registry.NewWithStore(*beaconAddr, *storePath)
78+
r.SetStaleNodeThreshold(*staleThreshold)
6079
r.SetBeaconStats(b)
6180
if *adminToken != "" {
6281
r.SetAdminToken(*adminToken)
6382
}
6483
if *dashboardToken != "" {
6584
r.SetDashboardToken(*dashboardToken)
6685
}
86+
// Banner persistence: derive sibling path from the registry store dir
87+
// so PUT /api/banner survives restarts. SetBannerPath reads the file
88+
// if present, overriding the flag default with the runtime value.
89+
if *storePath != "" {
90+
bp := filepath.Join(filepath.Dir(*storePath), "banner.txt")
91+
if *maintenanceBanner != "" {
92+
r.SetMaintenanceBanner(*maintenanceBanner) // flag-default first
93+
}
94+
r.SetBannerPath(bp) // then override from disk if file exists
95+
} else if *maintenanceBanner != "" {
96+
r.SetMaintenanceBanner(*maintenanceBanner)
97+
}
98+
if *replToken != "" {
99+
r.SetReplicationToken(*replToken)
100+
}
67101
if *enableTLS {
68102
if err := r.SetTLS(*tlsCert, *tlsKey); err != nil {
69103
log.Fatalf("TLS setup: %v", err)

0 commit comments

Comments
 (0)