feat: fleet ops, KEV scan, tunnels, beacon fallback, persistence
Extend owned-fleet control with scheduled tasks, audit log, file browser, HTTPS beacon when WS drops, protocol tunnels, registry/autostart forge options, KEV exposure in full sys check with Telegram alerts, and UI/tests.
This commit is contained in:
@@ -27,6 +27,13 @@ type Config struct {
|
||||
Background BackgroundConfig `json:"background,omitempty"`
|
||||
Alerts AlertsConfig `json:"alerts"`
|
||||
Server ServerSettings `json:"server"`
|
||||
TunnelDefaults TunnelDefaults `json:"tunnel_defaults,omitempty"`
|
||||
}
|
||||
|
||||
// TunnelDefaults holds operator-facing protocol tunnel presets (Calibrate).
|
||||
type TunnelDefaults struct {
|
||||
// CloudflaredTargetURL is the default outbound tunnel target (usually server public_url).
|
||||
CloudflaredTargetURL string `json:"cloudflared_target_url"`
|
||||
}
|
||||
|
||||
// ServerSettings controls the locally hosted control server (not baked into miners).
|
||||
@@ -110,6 +117,7 @@ type AlertsConfig struct {
|
||||
RejectionRateThresholdPct int `json:"rejection_rate_threshold_pct"`
|
||||
TelegramBotToken string `json:"telegram_bot_token"`
|
||||
TelegramChatID string `json:"telegram_chat_id"`
|
||||
WebhookURL string `json:"webhook_url"`
|
||||
// Per-event Telegram/email toggles (default true).
|
||||
NotifyAgentConnect bool `json:"notify_agent_connect"`
|
||||
NotifyAgentReconnect bool `json:"notify_agent_reconnect"`
|
||||
@@ -117,6 +125,7 @@ type AlertsConfig struct {
|
||||
NotifyHashrateDrop bool `json:"notify_hashrate_drop"`
|
||||
NotifyRejectionRate bool `json:"notify_rejection_rate"`
|
||||
NotifyBuildComplete bool `json:"notify_build_complete"`
|
||||
NotifyKEVExposure bool `json:"notify_kev_exposure"`
|
||||
EmailEnabled bool `json:"email_enabled"`
|
||||
SMTPHost string `json:"smtp_host"`
|
||||
SMTPPort int `json:"smtp_port"`
|
||||
@@ -194,6 +203,7 @@ func DefaultConfig() *Config {
|
||||
NotifyHashrateDrop: true,
|
||||
NotifyRejectionRate: true,
|
||||
NotifyBuildComplete: true,
|
||||
NotifyKEVExposure: true,
|
||||
},
|
||||
Server: ServerSettings{
|
||||
PublicURL: "",
|
||||
@@ -247,10 +257,15 @@ func LoadConfig() *Config {
|
||||
cfg.Alerts.NotifyHashrateDrop = true
|
||||
cfg.Alerts.NotifyRejectionRate = true
|
||||
cfg.Alerts.NotifyBuildComplete = true
|
||||
cfg.Alerts.NotifyKEVExposure = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if strings.TrimSpace(cfg.TunnelDefaults.CloudflaredTargetURL) == "" && strings.TrimSpace(cfg.Server.PublicURL) != "" {
|
||||
cfg.TunnelDefaults.CloudflaredTargetURL = strings.TrimSpace(cfg.Server.PublicURL)
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
@@ -262,6 +277,7 @@ func (c *Config) AlertSettings() alerts.Settings {
|
||||
return alerts.NewSettings(alerts.NotifyConfig{
|
||||
TelegramBotToken: c.Alerts.TelegramBotToken,
|
||||
TelegramChatID: c.Alerts.TelegramChatID,
|
||||
WebhookURL: c.Alerts.WebhookURL,
|
||||
EmailEnabled: c.Alerts.EmailEnabled,
|
||||
SMTPHost: c.Alerts.SMTPHost,
|
||||
SMTPPort: c.Alerts.SMTPPort,
|
||||
@@ -276,6 +292,7 @@ func (c *Config) AlertSettings() alerts.Settings {
|
||||
HashrateDrop: c.Alerts.NotifyHashrateDrop,
|
||||
RejectionRate: c.Alerts.NotifyRejectionRate,
|
||||
BuildComplete: c.Alerts.NotifyBuildComplete,
|
||||
KEVExposure: c.Alerts.NotifyKEVExposure,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -398,6 +415,9 @@ func mergeConfig(dst, src *Config) {
|
||||
if src.Alerts.TelegramChatID != "" {
|
||||
dst.Alerts.TelegramChatID = src.Alerts.TelegramChatID
|
||||
}
|
||||
if src.Alerts.WebhookURL != "" {
|
||||
dst.Alerts.WebhookURL = src.Alerts.WebhookURL
|
||||
}
|
||||
dst.Alerts.EmailEnabled = src.Alerts.EmailEnabled
|
||||
dst.Alerts.NotifyAgentConnect = src.Alerts.NotifyAgentConnect
|
||||
dst.Alerts.NotifyAgentReconnect = src.Alerts.NotifyAgentReconnect
|
||||
@@ -405,6 +425,7 @@ func mergeConfig(dst, src *Config) {
|
||||
dst.Alerts.NotifyHashrateDrop = src.Alerts.NotifyHashrateDrop
|
||||
dst.Alerts.NotifyRejectionRate = src.Alerts.NotifyRejectionRate
|
||||
dst.Alerts.NotifyBuildComplete = src.Alerts.NotifyBuildComplete
|
||||
dst.Alerts.NotifyKEVExposure = src.Alerts.NotifyKEVExposure
|
||||
if src.Alerts.SMTPHost != "" {
|
||||
dst.Alerts.SMTPHost = src.Alerts.SMTPHost
|
||||
}
|
||||
@@ -661,6 +682,9 @@ func mergeConfigExplicit(dst, src *Config, present map[string]json.RawMessage) {
|
||||
if in(alertKeys, "telegram_chat_id") && src.Alerts.TelegramChatID != "" {
|
||||
dst.Alerts.TelegramChatID = src.Alerts.TelegramChatID
|
||||
}
|
||||
if in(alertKeys, "webhook_url") && src.Alerts.WebhookURL != "" {
|
||||
dst.Alerts.WebhookURL = src.Alerts.WebhookURL
|
||||
}
|
||||
if in(alertKeys, "email_enabled") {
|
||||
dst.Alerts.EmailEnabled = src.Alerts.EmailEnabled
|
||||
}
|
||||
@@ -682,6 +706,9 @@ func mergeConfigExplicit(dst, src *Config, present map[string]json.RawMessage) {
|
||||
if in(alertKeys, "notify_build_complete") {
|
||||
dst.Alerts.NotifyBuildComplete = src.Alerts.NotifyBuildComplete
|
||||
}
|
||||
if in(alertKeys, "notify_kev_exposure") {
|
||||
dst.Alerts.NotifyKEVExposure = src.Alerts.NotifyKEVExposure
|
||||
}
|
||||
if in(alertKeys, "smtp_host") && src.Alerts.SMTPHost != "" {
|
||||
dst.Alerts.SMTPHost = src.Alerts.SMTPHost
|
||||
}
|
||||
@@ -762,6 +789,18 @@ func mergeConfigExplicit(dst, src *Config, present map[string]json.RawMessage) {
|
||||
dst.Server.FleetSecret = src.Server.FleetSecret
|
||||
}
|
||||
}
|
||||
|
||||
if has("tunnel_defaults") {
|
||||
tdKeys := nestedJSONKeys(present, "tunnel_defaults")
|
||||
if in(tdKeys, "cloudflared_target_url") {
|
||||
dst.TunnelDefaults.CloudflaredTargetURL = src.TunnelDefaults.CloudflaredTargetURL
|
||||
}
|
||||
}
|
||||
|
||||
// Keep cloudflared default aligned with public_url when unset.
|
||||
if strings.TrimSpace(dst.TunnelDefaults.CloudflaredTargetURL) == "" && strings.TrimSpace(dst.Server.PublicURL) != "" {
|
||||
dst.TunnelDefaults.CloudflaredTargetURL = strings.TrimSpace(dst.Server.PublicURL)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) Save() error {
|
||||
|
||||
Reference in New Issue
Block a user