Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
When ai_control_enabled and mining interrupts or hashrate drops, the server composes same-agent fix plans (container restart, chain reorder, GPU swap, idle tune, RandomX restart) with Seer and oath ledger audit — no spread or lateral escalation.
108 lines
2.5 KiB
Go
108 lines
2.5 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
|
|
"crypto-miner-server/internal/epidemiology"
|
|
"crypto-miner-server/internal/miningsurgery"
|
|
)
|
|
|
|
func (h *WSHub) miningSurgeryTracker() *miningsurgery.Tracker {
|
|
if h == nil {
|
|
return nil
|
|
}
|
|
return h.miningSurgery
|
|
}
|
|
|
|
// WireDefaultMiningSurgeryReporter connects self-surgery telemetry to Seer + oath ledger.
|
|
func (h *WSHub) WireDefaultMiningSurgeryReporter() {
|
|
if h == nil {
|
|
return
|
|
}
|
|
tr := h.miningSurgeryTracker()
|
|
if tr == nil {
|
|
return
|
|
}
|
|
tr.SetReporter(miningsurgery.Reporter{
|
|
OnSeer: func(ev miningsurgery.SeerEvent) {
|
|
h.broadcastSeerEvent(epidemiology.SeerEvent{
|
|
Kind: ev.Kind,
|
|
AgentID: ev.AgentID,
|
|
Payload: ev.Payload,
|
|
Timestamp: ev.Timestamp,
|
|
})
|
|
if h.db != nil {
|
|
_, _ = h.db.InsertSeerEvent(ev.Kind, ev.AgentID, ev.Payload)
|
|
}
|
|
},
|
|
OnOath: func(agentID, actor, actionType, strain, whyHash, outcome string, payload map[string]interface{}) {
|
|
if h.db == nil {
|
|
return
|
|
}
|
|
_, _ = h.db.InsertOathLedger(actor, actionType, agentID, strain, whyHash, outcome, payload)
|
|
},
|
|
})
|
|
}
|
|
|
|
func (h *WSHub) observeMiningSelfSurgeryFromStats(agentID string, stats epidemiology.StatsInput) {
|
|
if h == nil || agentID == "" {
|
|
return
|
|
}
|
|
if !h.serverPolicySnapshot().AIControlEnabled {
|
|
return
|
|
}
|
|
tr := h.miningSurgeryTracker()
|
|
if tr == nil {
|
|
return
|
|
}
|
|
idlePct := h.defaultIdleThresholdPct()
|
|
|
|
if state := epidemiology.DetectInterruption(agentID, stats); state != nil {
|
|
plan := miningsurgery.ComposeFromInterrupt(*state, idlePct)
|
|
tr.QueuePlan(plan)
|
|
return
|
|
}
|
|
if miningsurgery.DetectLowHashrate(stats, miningsurgery.DefaultLowHashrateHPS) {
|
|
hr := stats.MiningHashrate
|
|
if hr <= 0 {
|
|
hr = stats.Hashrate15m
|
|
}
|
|
plan := miningsurgery.ComposeFromLowHashrate(agentID, stats.ActiveMethod, hr, idlePct)
|
|
tr.QueuePlan(plan)
|
|
}
|
|
}
|
|
|
|
func (h *WSHub) defaultIdleThresholdPct() int {
|
|
return 20
|
|
}
|
|
|
|
func (h *WSHub) attachMiningSelfSurgery(resp map[string]interface{}, agentID string) {
|
|
tr := h.miningSurgeryTracker()
|
|
if tr == nil {
|
|
return
|
|
}
|
|
plan, ok := tr.ConsumePlan(agentID)
|
|
if !ok {
|
|
return
|
|
}
|
|
resp["mining_self_surgery"] = plan
|
|
}
|
|
|
|
func (h *WSHub) handleSelfSurgeryReport(agentID string, payload json.RawMessage) {
|
|
tr := h.miningSurgeryTracker()
|
|
if tr == nil {
|
|
return
|
|
}
|
|
var report struct {
|
|
Outcome string `json:"outcome"`
|
|
Results []map[string]interface{} `json:"results"`
|
|
}
|
|
if json.Unmarshal(payload, &report) != nil {
|
|
report.Outcome = "applied"
|
|
}
|
|
if report.Outcome == "" {
|
|
report.Outcome = "applied"
|
|
}
|
|
tr.RecordOutcome(agentID, report.Outcome, report.Results)
|
|
}
|