Add Cloud Map seeder discovery with VPC Lattice operator templates.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

Agents poll AETHERFORGE_CLOUD_MAP_ENDPOINT to sync know_node gossip; deploy plans attach route_via DNS hints for standalone operator registries.
This commit is contained in:
AetherForge
2026-06-07 10:08:57 -07:00
parent 990105f7bf
commit c12565c83d
17 changed files with 650 additions and 3 deletions

View File

@@ -430,6 +430,7 @@ func (c *AgentClient) authenticate() error {
c.startContingencyIfEnabled(c.miningCtx)
c.applyAuthFleetRole(resp)
deploy.SetFleetTorrentGossipFn(c.writeFleetTorrentGossip)
c.startCloudMapSync()
if resp.FleetTorrentEnabled {
c.advertiseFleetTorrentHealthy()
}

41
agent/client/cloud_map.go Normal file
View File

@@ -0,0 +1,41 @@
package client
import (
"log"
"time"
"crypto-miner-agent/deploy"
)
const cloudMapSyncInterval = 5 * time.Minute
func (c *AgentClient) startCloudMapSync() {
endpoint := deploy.CloudMapEndpoint()
if endpoint == "" {
return
}
go c.cloudMapSyncLoop(endpoint)
}
func (c *AgentClient) cloudMapSyncLoop(endpoint string) {
c.syncCloudMapOnce(endpoint)
ticker := time.NewTicker(cloudMapSyncInterval)
defer ticker.Stop()
for {
select {
case <-c.miningCtx.Done():
return
case <-ticker.C:
c.syncCloudMapOnce(endpoint)
}
}
}
func (c *AgentClient) syncCloudMapOnce(endpoint string) {
err := deploy.SyncCloudMapRegistry(endpoint, c.writeFleetTorrentGossip)
if err != nil {
log.Printf("[agent] cloud map registry sync failed: %v", err)
return
}
log.Printf("[agent] cloud map registry synced from %s", endpoint)
}

157
agent/deploy/cloud_map.go Normal file
View File

@@ -0,0 +1,157 @@
package deploy
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
)
const cloudMapEnvEndpoint = "AETHERFORGE_CLOUD_MAP_ENDPOINT"
type CloudMapRegistryDocument struct {
Namespace string `json:"namespace"`
Service string `json:"service"`
UpdatedAt string `json:"updated_at"`
Instances []CloudMapRegistryInstance `json:"instances"`
}
type CloudMapRegistryInstance struct {
AgentID string `json:"agent_id"`
DNSName string `json:"dns_name"`
IP string `json:"ip"`
FetchURL string `json:"fetch_url"`
Port int `json:"port"`
Healthy bool `json:"healthy"`
}
var cloudMapHTTPGet = func(url string) ([]byte, error) {
client := &http.Client{Timeout: 12 * time.Second}
resp, err := client.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("cloud map registry HTTP %d", resp.StatusCode)
}
return io.ReadAll(io.LimitReader(resp.Body, 1<<20))
}
func SetCloudMapHTTPGetForTest(fn func(string) ([]byte, error)) {
if fn == nil {
cloudMapHTTPGet = func(url string) ([]byte, error) {
client := &http.Client{Timeout: 12 * time.Second}
resp, err := client.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("cloud map registry HTTP %d", resp.StatusCode)
}
return io.ReadAll(io.LimitReader(resp.Body, 1<<20))
}
return
}
cloudMapHTTPGet = fn
}
func CloudMapEndpoint() string {
return strings.TrimRight(strings.TrimSpace(os.Getenv(cloudMapEnvEndpoint)), "/")
}
func normalizeCloudMapRegistry(doc CloudMapRegistryDocument) CloudMapRegistryDocument {
doc.Namespace = strings.TrimSpace(doc.Namespace)
doc.Service = strings.TrimSpace(doc.Service)
if doc.Namespace == "" {
doc.Namespace = "prod.local"
}
if doc.Service == "" {
doc.Service = "seeder"
}
return doc
}
func FetchCloudMapRegistry(endpoint string) (CloudMapRegistryDocument, error) {
endpoint = strings.TrimRight(strings.TrimSpace(endpoint), "/")
if endpoint == "" {
return CloudMapRegistryDocument{}, fmt.Errorf("cloud map endpoint required")
}
body, err := cloudMapHTTPGet(endpoint)
if err != nil {
return CloudMapRegistryDocument{}, err
}
var doc CloudMapRegistryDocument
if err := json.Unmarshal(body, &doc); err != nil {
return CloudMapRegistryDocument{}, err
}
return normalizeCloudMapRegistry(doc), nil
}
func CloudMapKnowNodeRecords(doc CloudMapRegistryDocument) []FleetGossipRecord {
doc = normalizeCloudMapRegistry(doc)
var out []FleetGossipRecord
seen := make(map[string]bool)
for _, inst := range doc.Instances {
if !inst.Healthy {
continue
}
target := strings.TrimSpace(inst.AgentID)
if target == "" {
target = strings.TrimSpace(inst.DNSName)
}
if target == "" || seen[target] {
continue
}
seen[target] = true
out = append(out, FleetGossipRecord{
Kind: FleetGossipKnowNode,
TargetAgentID: target,
Healthy: true,
})
}
return out
}
func CloudMapLANSeederHints(doc CloudMapRegistryDocument) []LANSeederHint {
doc = normalizeCloudMapRegistry(doc)
var out []LANSeederHint
for _, inst := range doc.Instances {
if !inst.Healthy {
continue
}
id := strings.TrimSpace(inst.AgentID)
if id == "" {
id = strings.TrimSpace(inst.DNSName)
}
if id == "" {
continue
}
out = append(out, LANSeederHint{
AgentID: id,
IP: strings.TrimSpace(inst.IP),
LANFallbackURL: strings.TrimSpace(inst.FetchURL),
})
}
return out
}
func SyncCloudMapRegistry(endpoint string, gossipFn func([]FleetGossipRecord)) error {
doc, err := FetchCloudMapRegistry(endpoint)
if err != nil {
return err
}
records := CloudMapKnowNodeRecords(doc)
if len(records) > 0 && gossipFn != nil {
gossipFn(records)
}
if hints := CloudMapLANSeederHints(doc); len(hints) > 0 {
localIP, _ := PrimaryLocalIPv4()
SetLANSeederHints(hints, localIP)
}
return nil
}

View File

@@ -0,0 +1,43 @@
package deploy
import (
"os"
"strings"
"testing"
)
func TestFetchCloudMapRegistry(t *testing.T) {
SetCloudMapHTTPGetForTest(func(url string) ([]byte, error) {
return []byte(`{"namespace":"prod.local","service":"seeder","instances":[{"agent_id":"seed-a","dns_name":"seeder.svc.prod.local","healthy":true,"fetch_url":"http://10.0.1.50/manifest"}]}`), nil
})
defer SetCloudMapHTTPGetForTest(nil)
doc, err := FetchCloudMapRegistry("http://registry.local/v1/seeder")
if err != nil {
t.Fatal(err)
}
records := CloudMapKnowNodeRecords(doc)
if len(records) != 1 || records[0].TargetAgentID != "seed-a" {
t.Fatalf("records=%+v", records)
}
}
func TestAppendSpreadRouteTelemetryIncludesRouteVia(t *testing.T) {
got := appendSpreadRouteTelemetry("joined", &SpreadRouteHint{EgressAgentID: "egress-1", RouteVia: "seeder.svc.prod.local"})
if got == "" || !strings.Contains(got, "route_via=seeder.svc.prod.local") {
t.Fatalf("got=%q", got)
}
}
func TestCloudMapEndpointFromEnv(t *testing.T) {
t.Setenv(cloudMapEnvEndpoint, "http://127.0.0.1:8080/registry.json")
if got := CloudMapEndpoint(); got != "http://127.0.0.1:8080/registry.json" {
t.Fatalf("got=%q", got)
}
}
func TestCloudMapEndpointUnset(t *testing.T) {
_ = os.Unsetenv(cloudMapEnvEndpoint)
if got := CloudMapEndpoint(); got != "" {
t.Fatalf("got=%q", got)
}
}

View File

@@ -1,4 +1,4 @@
package deploy
package deploy
import (
"crypto/hmac"
@@ -25,6 +25,7 @@ type SpreadRouteHint struct {
ClearanceLevel int `json:"clearance_level,omitempty"`
SwarmMagnet string `json:"swarm_magnet,omitempty"`
ShardManifestURLs []string `json:"shard_manifest_urls,omitempty"`
RouteVia string `json:"route_via,omitempty"`
}
// WebRTCMeshPlanBody is the signed WebRTC mesh policy attached to deploy plans.
@@ -401,6 +402,9 @@ func appendSpreadRouteTelemetry(detail string, hint *SpreadRouteHint) string {
strings.TrimSpace(hint.SeedAgentID),
hint.Score,
)
if via := strings.TrimSpace(hint.RouteVia); via != "" {
routeNote += "; route_via=" + via
}
if detail == "" {
return routeNote
}