Documentation
¶
Overview ¶
Package dashboard implements the HTTP dashboard server, probe loop, and pulse-sample ring for the Pilot Protocol registry.
Thread safety: all exported Handler methods are safe for concurrent use.
Index ¶
- Constants
- func WriteBucketedStats(ring []StatsSample, idxPtr *int, sample StatsSample, bucketSecs int64)
- type AuditEntrySnapshot
- type BackupEntry
- type BeaconStatsProvider
- type BreakerListEntry
- type Callbacks
- type DashboardStats
- type Handler
- func (h *Handler) GetProbeStates() map[string]*ProbeState
- func (h *Handler) GetPulseSamples() []PulseSample
- func (h *Handler) MaintenanceBanner() string
- func (h *Handler) ProbeLoop()
- func (h *Handler) PulseLoop()
- func (h *Handler) RecentThroughput(window int) float64
- func (h *Handler) Serve(addr string) error
- func (h *Handler) SetBackupsList(fn func() ([]BackupEntry, error))
- func (h *Handler) SetBannerPath(path string)
- func (h *Handler) SetHTTPProbeAddr(addr string)
- func (h *Handler) SetLogLevelCallbacks(get func() string, set func(string) error)
- func (h *Handler) SetMaintenanceBanner(msg string)
- func (h *Handler) SetProbeStates(states map[string]*ProbeState)
- func (h *Handler) SetReplicationStatus(fn func() ReplicationSnapshot)
- func (h *Handler) SetWhitelistCallbacks(get func() ([]byte, error), set func([]byte) error)
- func (h *Handler) StatsCollectorLoop(readyCh <-chan struct{}, done <-chan struct{}, ...)
- type MemberSnapshot
- type NetHistoryRing
- type NetworkSampleEntry
- type NetworkSnapshot
- type NetworkStats
- type NodeSnapshot
- type ProbeState
- type PulseSample
- type ReleaseBanner
- type ReplicationSnapshot
- type StatsSample
- type StatsSampleResult
Constants ¶
const ProbeRetention = 30 * 24 * time.Hour
ProbeRetention is the maximum age of probe downtime intervals kept in memory and persisted to snapshots.
Variables ¶
This section is empty.
Functions ¶
func WriteBucketedStats ¶
func WriteBucketedStats(ring []StatsSample, idxPtr *int, sample StatsSample, bucketSecs int64)
WriteBucketedStats writes a sample into a fixed ring buffer with time-bucket dedup: if the most recently written slot holds a sample in the same bucket, it is overwritten in place instead of advancing the index. This prevents duplicate entries (e.g. two samples on the same UTC day after a restart).
Types ¶
type AuditEntrySnapshot ¶ added in v0.2.5
type AuditEntrySnapshot struct {
Timestamp string `json:"timestamp"`
Action string `json:"action"`
NetworkID uint16 `json:"network_id,omitempty"`
NodeID uint32 `json:"node_id,omitempty"`
Details string `json:"details,omitempty"`
Hash string `json:"hash,omitempty"`
}
AuditEntrySnapshot is the JSON-friendly shape returned by /api/admin/audit/recent. Lifted out of the server package so the dashboard doesn't import it directly. Fields mirror audit.Entry so the wire format is stable across releases.
type BackupEntry ¶ added in v0.2.5
type BackupEntry struct {
Name string `json:"name"`
SizeBytes int64 `json:"size_bytes"`
ModUnix int64 `json:"mod_unix"`
}
BackupEntry is the wire shape returned per file by /api/admin/backups.
type BeaconStatsProvider ¶
type BeaconStatsProvider interface {
RelayForwarded() uint64
RelayDropped() uint64
RelayNotFound() uint64
}
BeaconStatsProvider exposes relay counters the dashboard surfaces in /api/stats. Implemented by pkg/beacon.Server.
type BreakerListEntry ¶ added in v0.2.5
type BreakerListEntry struct {
Name string `json:"name"`
State string `json:"state"`
Reason string `json:"reason,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
AllowedTotal int64 `json:"allowed_total"`
DeniedTotal int64 `json:"denied_total"`
LastDeniedAt *time.Time `json:"last_denied_at,omitempty"`
}
BreakerListEntry is one row in the /api/breakers response. Marshalled as-is to JSON, so field tags pin the public wire format. Counters are monotonic since process start; LastDeniedAt is nil when DeniedTotal is 0.
type Callbacks ¶
type Callbacks struct {
// GetDashboardToken returns the current dashboard token.
GetDashboardToken func() string
// GetAdminToken returns the current admin token.
GetAdminToken func() string
// BuildStatsPayload assembles the core /api/stats JSON payload (without
// probe state and maintenance banner, which are owned by Handler).
// The returned map is JSON-safe; callers must not mutate it.
BuildStatsPayload func(authenticated bool) map[string]interface{}
// GetNodes returns a snapshot of registered nodes for /api/nodes.
GetNodes func() []NodeSnapshot
// RequestCount returns the current total request count.
RequestCount func() int64
// StartTime returns when the server started.
StartTime func() time.Time
// NodeCount returns the number of registered nodes currently in the
// in-memory map. Decays toward OnlineCount as the reaper deletes
// stale entries (see server_lifecycle.reapStaleNodes).
NodeCount func() int
// OnlineCount returns the number of nodes online at or after threshold.
OnlineCount func(threshold time.Time) int
// TotalEverRegistered returns the cumulative count of node IDs ever
// allocated by this registry (monotonic across reaps, persisted
// across restarts via the snapshot's next_node counter). Surfaced on
// /api/public-stats as total_nodes so it stays distinct from
// active_nodes (which post-reap is identical to NodeCount).
TotalEverRegistered func() int64
// BuildInfo returns the build-time identity of the running binary
// (version, git_commit, build_time, binary_sha256, go_version) so
// /api/public-stats can publish a verifiable record of what code is
// actually running. Returns nil when build info wasn't registered;
// the handler then omits the build_info field entirely.
BuildInfo func() map[string]string
// StaleThreshold returns the configured stale-node threshold.
StaleThreshold func() time.Duration
// TriggerSnapshot triggers a snapshot save.
TriggerSnapshot func() error
// UpdateGauges updates Prometheus gauges before /metrics scrape.
UpdateGauges func()
// WriteMetrics writes Prometheus metrics to w.
WriteMetrics func(w io.Writer)
// ListenerAddr returns the registry TCP listener address for probeRegistry.
// Returns "" when no listener is bound.
ListenerAddr func() string
// BeaconAddr returns the beacon UDP address for probeBeacon. "" = disabled.
BeaconAddr func() string
// ReadyCh returns the channel closed when the server is ready.
ReadyCh func() <-chan struct{}
// Done returns the channel closed on server shutdown.
Done func() <-chan struct{}
// Save triggers a debounced snapshot after probe state changes.
Save func()
// BreakerAllow consults the Server's breaker manager for the named
// switch. Returns (allow, reason). Allowed=true means the gated path
// should proceed; allowed=false means it should return 503 to the
// caller with reason surfaced. Optional — when nil, every name is
// implicitly allowed (default-Closed semantics, same as a missing
// breakers.json).
BreakerAllow func(name string) (allowed bool, reason string)
// BreakerList returns the live snapshot for /api/breakers — one
// entry per known name with state, reason, counters, last-denied
// timestamp. Required for the read endpoint; nil disables it.
BreakerList func() []BreakerListEntry
// BreakerSet upserts one breaker by name, returning an error when
// the state value is malformed. Reason is operator free-text. Used
// by PUT /api/breakers/<name>. Writes to breakers.json on disk so
// the change survives restart; the file watcher's next poll is a
// no-op due to the matching mtime.
BreakerSet func(name, state, reason string) error
// BreakerDelete removes one breaker by name. Counters are
// preserved so post-incident review still shows traffic that
// flowed through the gate. Persists the change to breakers.json.
BreakerDelete func(name string) error
// HealthSnapshot returns a flat map of operator-facing health
// signals: snapshot save telemetry, WAL size, snapshot.write
// breaker state, replica subscriber count. Backs /api/health.
// Optional — when nil, /api/health 503s.
HealthSnapshot func() map[string]any
// AuditRecent returns the most recent N audit-log entries from the
// in-memory ring buffer (caller-supplied bound, server-clamped to a
// safe maximum). Optional — when nil, /api/admin/audit/recent
// returns an empty list.
AuditRecent func(n int) []AuditEntrySnapshot
// WhitelistGet returns the on-disk rate-limit whitelist as raw JSON
// bytes (the exact contents of /var/lib/pilot/rate-limit-whitelist.json).
// Optional — when nil, GET /api/admin/whitelist returns 404. When the
// file does not exist on disk, the callback should return ([]byte("[]"),
// nil) so the editor sees an empty list rather than an error.
WhitelistGet func() ([]byte, error)
// WhitelistSet atomically replaces the whitelist file with the given
// JSON bytes. The on-disk watcher (cmd/rendezvous/whitelist_watcher.go)
// picks the change up on its next 2 s poll, so the limiter is updated
// without a restart. Optional — when nil, PUT /api/admin/whitelist
// returns 405.
WhitelistSet func(data []byte) error
// GetLogLevel returns the current slog level ("debug", "info", "warn",
// "error"). Optional — when nil, GET /api/admin/runtime/log-level
// returns the static string "unknown".
GetLogLevel func() string
// SetLogLevel re-installs the default slog handler at the requested
// level. The level string must be one of debug/info/warn/error
// (case-insensitive); anything else returns an error and the previous
// handler is left in place. Optional — when nil, PUT
// /api/admin/runtime/log-level returns 405.
SetLogLevel func(level string) error
// ReplicationStatus returns a snapshot of the registry's replication
// state — current term, standby flag, replica subscriber count, and
// last replica push timestamp. Optional — when nil,
// GET /api/admin/replication-status returns 404.
ReplicationStatus func() ReplicationSnapshot
// BackupsList returns metadata for snapshot backup files. Used to power
// the dashboard's backup browser. Optional — when nil,
// GET /api/admin/backups returns 404.
BackupsList func() ([]BackupEntry, error)
// MembersList returns a snapshot of the membership for the given
// network — one entry per node with role, hostname (if registered),
// and last-seen unix timestamp. Backs the GET
// /api/admin/networks/{id}/members endpoint, which the dashboard's
// operator-facing member browser consumes. Optional — when nil, the
// endpoint returns 404 so a registry running without membership
// admin wiring still serves the rest of the dashboard cleanly.
// Errors are returned to the client as 500; protocol.ErrNetworkNotFound
// surfaces as 404.
MembersList func(netID uint16) ([]MemberSnapshot, error)
// NetworksList returns a snapshot of every known network with both
// the numeric ID and the human-readable name, plus per-role counts
// and the enterprise / policy-set flags. Powers the dashboard's
// network picker — without this the per-metric rollup (keyed by
// label = name) doesn't know which numeric ID the management
// endpoints (kick / set-role) need.
// Backs GET /api/admin/networks. Optional; nil → endpoint 404.
NetworksList func() []NetworkSnapshot
// MemberKick removes the given node from the network's member list
// and role map. The reason string is the operator's audit note (why
// the action was taken) and is required — endpoints reject empty
// reasons with 400 so post-incident review always has a paper trail.
// Backs DELETE /api/admin/networks/{id}/members/{nodeID}?reason=...
// Optional — when nil, the endpoint returns 404. Errors surface as
// 400 (cannot kick owner, unknown member) or 500 (generic failures);
// protocol.ErrNetworkNotFound surfaces as 404.
MemberKick func(netID uint16, nodeID uint32, reason string) error
// MemberRole updates the per-network role for the given node. Role
// must be "admin" or "member" (case-insensitive); anything else
// returns 400. Cannot demote the network owner — returns an error
// the handler surfaces as 400. Reason is the operator's audit note.
// Backs PUT /api/admin/networks/{id}/members/{nodeID}/role.
// Optional — when nil, the endpoint returns 404. The role-validation
// step is split between the dashboard handler (string accept set)
// and the server callback (owner-demotion check) so the wire format
// is policed at the edge.
MemberRole func(netID uint16, nodeID uint32, role, reason string) error
}
Callbacks bundles the side-effect functions Handler calls on the parent server. All functions must be safe for concurrent use.
type DashboardStats ¶
type DashboardStats struct {
TotalNodes int `json:"total_nodes"`
ActiveNodes int `json:"active_nodes"`
TotalTrustLinks int `json:"-"`
TotalRequests int64 `json:"total_requests"`
RelayForwarded uint64 `json:"relay_forwarded,omitempty"`
RelayDropped uint64 `json:"relay_dropped,omitempty"`
RelayNotFound uint64 `json:"relay_not_found,omitempty"`
ReqPerDay int64 `json:"req_per_day"`
UptimeSecs int64 `json:"uptime_secs"`
Versions map[string]int `json:"versions,omitempty"`
Networks []NetworkStats `json:"networks,omitempty"` // only populated with dashboard token
Hourly []StatsSample `json:"hourly,omitempty"`
Daily []StatsSample `json:"daily,omitempty"`
RestartEvents []int64 `json:"restart_events,omitempty"`
DowntimeIntervals [][2]int64 `json:"downtime_intervals,omitempty"`
Probes map[string]*ProbeState `json:"probes,omitempty"`
ReleaseBanner *ReleaseBanner `json:"release_banner,omitempty"`
}
DashboardStats is the public-safe data returned by the dashboard API.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler owns the dashboard HTTP mux, probe state, pulse ring, and the maintenance banner. It is wired into the parent server via Callbacks.
func NewHandler ¶
NewHandler creates a ready-to-use dashboard Handler backed by cb.
func (*Handler) GetProbeStates ¶
func (h *Handler) GetProbeStates() map[string]*ProbeState
GetProbeStates returns a deep-copy snapshot of all probe states.
func (*Handler) GetPulseSamples ¶
func (h *Handler) GetPulseSamples() []PulseSample
GetPulseSamples returns the ordered pulse samples from the ring buffer.
func (*Handler) MaintenanceBanner ¶
MaintenanceBanner returns the currently-set maintenance notice (or "").
func (*Handler) ProbeLoop ¶
func (h *Handler) ProbeLoop()
ProbeLoop runs the 4 component probes at a steady cadence. Must be called in a separate goroutine (typically by the parent server).
func (*Handler) PulseLoop ¶
func (h *Handler) PulseLoop()
PulseLoop records one request-count sample per second into the ring. Must be called in a separate goroutine.
func (*Handler) RecentThroughput ¶ added in v0.2.5
RecentThroughput returns smoothed requests-per-second over the last `window` pulse samples (1 sample/sec). Returns 0 when there aren't yet two samples to differentiate or the span is zero.
func (*Handler) SetBackupsList ¶ added in v0.2.5
func (h *Handler) SetBackupsList(fn func() ([]BackupEntry, error))
SetBackupsList wires the GET /api/admin/backups endpoint. The closure owns the directory path; the dashboard package only enumerates what it returns.
func (*Handler) SetBannerPath ¶
SetBannerPath wires a disk-persistence path for the maintenance banner. If the file already exists, its contents become the in-memory banner.
func (*Handler) SetHTTPProbeAddr ¶
SetHTTPProbeAddr records the dashboard HTTP listen address so the probe loop knows where to dial for the dashboard + metrics probes.
func (*Handler) SetLogLevelCallbacks ¶ added in v0.2.5
SetLogLevelCallbacks wires the GET/PUT /api/admin/runtime/log-level endpoints after construction. The level lives in cmd/rendezvous/main.go (alongside logging.Setup) so cannot be set inside NewWithStore.
func (*Handler) SetMaintenanceBanner ¶
SetMaintenanceBanner sets a free-form notice rendered on the dashboard. Empty string clears it. If a bannerPath has been configured, the new value is atomically written to disk.
func (*Handler) SetProbeStates ¶
func (h *Handler) SetProbeStates(states map[string]*ProbeState)
SetProbeStates replaces the probe state map (used during snapshot restore).
func (*Handler) SetReplicationStatus ¶ added in v0.2.5
func (h *Handler) SetReplicationStatus(fn func() ReplicationSnapshot)
SetReplicationStatus wires the GET /api/admin/replication-status endpoint.
func (*Handler) SetWhitelistCallbacks ¶ added in v0.2.5
SetWhitelistCallbacks wires the GET/PUT /api/admin/whitelist endpoints after construction. The Handler is created early in NewWithStore (before main.go knows the whitelist path), so the cmd-level package installs the callbacks once both pieces exist. Either argument may be nil to leave that direction disabled.
func (*Handler) StatsCollectorLoop ¶
func (h *Handler) StatsCollectorLoop( readyCh <-chan struct{}, done <-chan struct{}, sampleFn func() StatsSampleResult, recordFn func(StatsSampleResult, bool), )
StatsCollectorLoop runs in the background and samples stats for history charts. Hourly samples are taken every hour; daily samples every 24 hours.
readyCh is closed when the parent server is ready to serve (load complete). done is closed on server shutdown. sampleFn returns a fresh StatsSampleResult under whatever locking the caller requires. recordFn persists the sample into the parent server's ring buffers; it must acquire any required server locks internally.
type MemberSnapshot ¶ added in v0.2.5
type MemberSnapshot struct {
NodeID uint32 `json:"node_id"`
Role string `json:"role"`
Hostname string `json:"hostname,omitempty"`
LastSeenUnix int64 `json:"last_seen_unix,omitempty"`
}
MemberSnapshot is one row in the GET /api/admin/networks/{id}/members response. Hostname and LastSeenUnix are omitted when zero so the wire payload stays compact for members that have never registered a hostname or whose last_seen is unknown.
type NetHistoryRing ¶
type NetHistoryRing struct {
Samples []NetworkSampleEntry // length == Size; heap-allocated per ring
Size int // ring capacity (24 for hourly, 30 for daily)
Idx int // next write index
}
NetHistoryRing is a fixed-size ring buffer for per-network history samples.
func NewNetHistoryRing ¶
func NewNetHistoryRing(size int) *NetHistoryRing
NewNetHistoryRing allocates a new ring of the given capacity.
func (*NetHistoryRing) Read ¶
func (r *NetHistoryRing) Read() []NetworkSampleEntry
Read returns all non-zero entries in chronological order.
func (*NetHistoryRing) Write ¶
func (r *NetHistoryRing) Write(e NetworkSampleEntry)
Write appends a new entry, overwriting the oldest on wraparound.
func (*NetHistoryRing) WriteBucketed ¶
func (r *NetHistoryRing) WriteBucketed(e NetworkSampleEntry, bucketSecs int64)
WriteBucketed overwrites the most recent entry if its timestamp falls in the same time bucket (e.g. 86400s for per-day dedup); otherwise behaves like Write. Guarantees at most one entry per bucket across restarts.
type NetworkSampleEntry ¶
type NetworkSampleEntry struct {
Timestamp int64 `json:"ts"`
ID uint16 `json:"id"`
Name string `json:"name"`
Members int `json:"members"`
Online int `json:"online"`
Requests int64 `json:"requests"`
}
NetworkSampleEntry holds per-network stats within a time-series sample.
type NetworkSnapshot ¶ added in v0.2.5
type NetworkSnapshot struct {
ID uint16 `json:"id"`
Name string `json:"name"`
MembersCount int `json:"members_count"`
AdminsCount int `json:"admins_count"`
OwnersCount int `json:"owners_count"`
Enterprise bool `json:"enterprise"`
PolicySet bool `json:"policy_set"`
}
NetworkSnapshot is one row in the GET /api/admin/networks response. Powers the dashboard's network picker: the labeled /metrics rollup is keyed by network NAME but the management endpoints (kick / set role) take numeric IDs, so this endpoint surfaces both alongside the per-network role counts and the enterprise / policy-set flags.
type NetworkStats ¶
type NetworkStats struct {
ID uint16 `json:"id"`
Name string `json:"name"`
Members int `json:"members"`
Online int `json:"online"`
Requests int64 `json:"requests"`
TrustLinks int `json:"-"`
Hourly []NetworkSampleEntry `json:"hourly,omitempty"`
Daily []NetworkSampleEntry `json:"daily,omitempty"`
}
NetworkStats holds per-network statistics for the authenticated dashboard view.
type NodeSnapshot ¶
NodeSnapshot is a minimal node record for the /api/nodes endpoint.
type ProbeState ¶
type ProbeState struct {
LastSuccess int64 `json:"last_success,omitempty"` // millis
DowntimeIntervals [][2]int64 `json:"downtime_intervals,omitempty"` // pruned to 30d
CurrentDownStart int64 `json:"current_down_start,omitempty"` // 0 when up
}
ProbeState holds the health history for a single named probe.
type PulseSample ¶
PulseSample is one entry in the server-side 1/sec request-count ring.
type ReleaseBanner ¶
type ReleaseBanner struct {
Version string `json:"version"`
PublishedAt int64 `json:"published_at"`
}
ReleaseBanner is the public-safe snapshot of the latest GitHub release. Surfaced on the dashboard so users see why peers may be briefly unreachable while the fleet upgrades.
type ReplicationSnapshot ¶ added in v0.2.5
type ReplicationSnapshot struct {
Term uint64 `json:"term"`
IsStandby bool `json:"is_standby"`
Subscribers int `json:"subscribers"`
LastPushUnix int64 `json:"last_push_unix,omitempty"`
LastPushAgeSecs int64 `json:"last_push_age_secs,omitempty"`
}
ReplicationSnapshot is the wire shape returned by /api/admin/replication-status.
type StatsSample ¶
type StatsSample struct {
Timestamp int64 `json:"ts"`
TotalNodes int `json:"total_nodes"`
OnlineNodes int `json:"online_nodes"`
TrustLinks int `json:"trust_links,omitempty"`
TotalRequests int64 `json:"total_requests"`
}
StatsSample is a single time-series data point for dashboard history charts.
type StatsSampleResult ¶
type StatsSampleResult struct {
Global StatsSample
Networks map[uint16]NetworkSampleEntry
}
StatsSampleResult holds both global and per-network samples from a single snapshot. It is produced by the server's sampleStats callback and consumed by recordSample.