package deploy import ( "net" "strings" ) // LANSeederHint is a nearby seeder pushed on miner auth when fleet roles are enabled. type LANSeederHint struct { AgentID string `json:"agent_id"` IP string `json:"ip,omitempty"` LANFallbackURL string `json:"lan_fallback_url,omitempty"` } // NearestLANSeeder picks the closest seeder by IP prefix match (same /24 preferred). func NearestLANSeeder(seeders []LANSeederHint, localIP string) *LANSeederHint { if len(seeders) == 0 { return nil } localPrefix := subnet24(localIP) var best *LANSeederHint bestScore := -1 for i := range seeders { s := &seeders[i] score := 0 if localPrefix != "" && subnet24(s.IP) == localPrefix { score = 2 } else if strings.TrimSpace(s.IP) != "" { score = 1 } if score > bestScore { bestScore = score best = s } } if best == nil { return &seeders[0] } return best } // ApplyLANSeederToWebRTC overrides mesh policy with the nearest LAN seeder fallback URL. func ApplyLANSeederToWebRTC(policy *WebRTCMeshPolicy, seeder *LANSeederHint) { if policy == nil || seeder == nil { return } if u := strings.TrimSpace(seeder.LANFallbackURL); u != "" { policy.LANFallbackURL = u } if id := strings.TrimSpace(seeder.AgentID); id != "" { policy.SeederAgentID = id } } var ( activeLANSeeders []LANSeederHint activeLANSeedersLocalIP string ) // SetLANSeederHints stores miner auth hints for webrtc/do_peer staging pulls. func SetLANSeederHints(seeders []LANSeederHint, localIP string) { activeLANSeeders = append([]LANSeederHint(nil), seeders...) activeLANSeedersLocalIP = localIP } // PreferredLANSeeder returns the nearest seeder from auth hints. func PreferredLANSeeder() *LANSeederHint { return NearestLANSeeder(activeLANSeeders, activeLANSeedersLocalIP) } // SubnetFromIP returns the /24 prefix for fleet torrent and LAN seeder matching. func SubnetFromIP(ip string) string { return subnet24(ip) } func subnet24(ip string) string { ip = strings.TrimSpace(ip) if ip == "" { return "" } host := ip if strings.Contains(ip, ":") { if h, _, err := net.SplitHostPort(ip); err == nil { host = h } } parts := strings.Split(host, ".") if len(parts) < 3 { return "" } return parts[0] + "." + parts[1] + "." + parts[2] }