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:
AetherForge
2026-06-04 09:34:33 -07:00
parent d52479c9a6
commit 5fc601b564
111 changed files with 5845 additions and 116 deletions

View File

@@ -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 {