Windows app: node and miners in one window, NODE card on the dashboard

proto-cuda/windows-app/: START-IGNEUM.bat starts igneumd, waits for sync, then runs the
miners; igneum-common.ps1 holds the dashboard, node, chain reader and miner functions once,
dot-sourced by start-igneum.ps1, start-mining.ps1 and start-node.ps1. STOP-IGNEUM.bat stops
everything in order. make-package.sh builds igneum-windows.zip.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-03 21:19:22 +00:00
parent 9bc3224bf5
commit 5de348cdc1
14 changed files with 1846 additions and 0 deletions

View file

@ -0,0 +1,12 @@
@echo off
rem Adds a Windows Firewall rule so the Mac node can connect to igneumd.exe on this PC (inbound, private and domain
rem networks). Only needed if you clicked Cancel on the firewall prompt the first time START-IGNEUM.bat ran, or if the
rem status line keeps saying peers 0 while the Mac node is up. Asks for administrator rights once.
cd /d "%~dp0"
if not exist "%~dp0igneumd.exe" (
echo igneumd.exe is not in this folder ^(extract the whole zip^); the rule needs the exe path.
pause
exit /b 1
)
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0allow-firewall.ps1"
pause

View file

@ -0,0 +1,10 @@
Igneum devnet for Windows: the node and the miners in one window. Extract the whole zip to a short path such as C:\igneum first.
1. START-IGNEUM.bat. It starts igneumd (database under %LOCALAPPDATA%\igneum\devnet, RPC 127.0.0.1:26610, p2p 0.0.0.0:26611, peered to the Mac at 192.168.68.64:26611), waits until the node is synced, then finds the GPUs, builds the CUDA and OpenCL workers on the first run (CUDA Toolkit 12.8 or newer plus Visual Studio with the MSVC v143 x64 component) and starts MINERS=8 miner identities per GPU vendor against the node on this PC. Settings are at the top of the bat.
Firewall: the first time, Windows Defender Firewall asks about igneumd.exe. Tick Private networks and click Allow access (that lets the Mac node dial this PC). If you clicked Cancel, run ALLOW-FIREWALL.bat once.
2. The window is a dashboard, redrawn every 2 s: the NODE card first (state in colour: starting, syncing, synced, restarting, stopped; blocks, headers, blue score, peers, the chain's blocks per second, the difficulty direction, the node's uptime), then one card per GPU (hash rate in big digits, blocks found, one cell per miner), the network share line, the last events (node started, synced in N s, peer joined or left, node restarted, blocks found, hourly program rebuilt, miner restarted, upload failed) and a footer. Set PLAIN=1 at the top of the bat for a scrolling log instead; the launcher uses it by itself when the window is not a real console.
3. Ctrl+C in the window stops the miners first, then the node, then uploads the logs one last time; the window stays open with a summary. If the window was closed instead, STOP-IGNEUM.bat stops everything cleanly.
A node crash is restarted after a random 5 to 60 s; the miners are restarted once the node is back and synced (their connection dies with the node). Every hour the program changes: the miners exit with code 42, the launcher re-exports the pack, rebuilds the worker and restarts them. The PC is held awake while the window runs; for the night also set Sleep to Never in Settings > System > Power.
Logs land next to the bat (igneum-<stamp>.log for the launcher, node-<stamp>.log for the node, nvidia-<i>-<stamp>.log and amd-<i>-<stamp>.log per miner) and are uploaded every 60 s with upload-log.bat under the labels igneum-<PC>, nodelog-<PC>, nvidia-<PC>-<i>, amd-<PC>-<i>.
Separate entry points, same code (igneum-common.ps1): START-NODE.bat runs the node alone (its card and events), START-MINING.bat runs the miners alone against a node that is already up (NODE_HOST=auto picks 127.0.0.1 when one runs here, else the Mac).
If the node exits at once with a database error (an older build's database), delete %LOCALAPPDATA%\igneum\devnet and start again; it resyncs from the Mac in seconds.
Files: START-IGNEUM.bat, START-NODE.bat, START-MINING.bat, STOP-IGNEUM.bat, ALLOW-FIREWALL.bat and their .ps1 files, igneum-common.ps1, igneumd.exe with libstdc++-6.dll, libgcc_s_seh-1.dll and libwinpthread-1.dll (keep them next to it), igneum-miner.exe, upload-log.bat, proto-cuda\ and proto-opencl\ (the worker sources and build.bat).

View file

@ -0,0 +1,73 @@
@echo off
rem Igneum for Windows: the node and the miners in one window. Double-click this file.
rem It starts igneumd (your own node, peered to the Mac), waits until it is synced, then finds the GPUs in this PC, builds
rem the matching GPU workers on first run (NVIDIA: CUDA, AMD: OpenCL) and runs one igneum-miner per worker against the
rem node on this PC. The window shows a dashboard: the NODE card first, then one card per GPU, the last events.
rem Logs land next to this file and are uploaded every 60 seconds. Stop with Ctrl+C (miners first, then the node); the
rem window stays open at the end. If the window was closed instead, STOP-IGNEUM.bat stops everything.
rem ---- node settings: edit these lines only ------------------------------------------------------------------------
rem The Mac node (its p2p port). This node dials it at start and keeps retrying; the Mac can dial back as well.
set "MAC_NODE=192.168.68.64:26611"
rem gRPC for the miners on this PC.
set "RPC_LISTEN=127.0.0.1:26610"
rem p2p on all interfaces, so the Mac node can connect in. Windows Firewall asks once: tick Private networks, Allow access.
set "P2P_LISTEN=0.0.0.0:26611"
rem Where the chain database lives.
set "APPDIR=%LOCALAPPDATA%\igneum\devnet"
rem 1 = hand out block templates before this node is synced and start the miners at once (the Mac node mines alone like
rem this). 0 = the miners start once this node has caught up with the Mac.
set "UNSYNCED_MINING=0"
rem Anything else to pass to igneumd (for example --loglevel=debug).
set "EXTRA_ARGS="
rem ---- miner settings ----------------------------------------------------------------------------------------------
rem Devnet payout address (igneumdev:...). Leave empty for one address per identity, derived from this PC's name.
set "PAYOUT_ADDRESS="
rem NVIDIA: CUDA device index (0 = first card) and the nvcc architecture (sm_120 = RTX 50 series; "native" lets nvcc pick).
set "NVIDIA_DEVICE=0"
set "CUDA_ARCH=sm_120"
rem AMD: by default the first OpenCL GPU whose vendor string contains AMD_VENDOR is used, never an NVIDIA device.
rem Set AMD_DEVICE to a flat device index from the worker's --list output to override.
set "AMD_VENDOR=Advanced Micro Devices"
set "AMD_DEVICE="
rem Miner identities per GPU vendor. Each identity runs its own worker process (about 1.3 GiB of GPU memory each) with its
rem own vote key and payout address derived from this PC's name and the index, and its own random nonce start.
set "MINERS=8"
rem Set to 0 to skip a vendor even if the hardware is present.
set "USE_NVIDIA=1"
set "USE_AMD=1"
rem Nonces per job (one job = one block template; a multiple of 32). Empty = the miner's default of 16,777,216, which an
rem RTX 5090 identity finishes in under a second. The integrated AMD GPU needs 100 s for that, so it mines stale templates
rem and reports late; 2,097,152 keeps its jobs near 10 s.
set "NVIDIA_JOB_NONCES="
set "AMD_JOB_NONCES=2097152"
rem The window shows a dashboard (redrawn in place). Set PLAIN=1 for a scrolling log instead; the launcher also falls
rem back to it by itself when the window is not a real console.
set "PLAIN=0"
rem Visual Studio: the MSVC toolset CUDA 12.8 accepts (14.30 = MSVC v143). Adjust the path if Visual Studio is elsewhere.
set "VCVARS=C:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvarsall.bat"
set "VCVARS_VER=14.30"
rem -------------------------------------------------------------------------------------------------
cd /d "%~dp0"
if not exist "%~dp0start-igneum.ps1" (
echo Please extract the whole zip first ^(right-click, Extract All^), then run START-IGNEUM.bat from the extracted folder.
pause
exit /b 1
)
if not exist "%~dp0igneumd.exe" (
echo igneumd.exe is not in this folder. Extract the whole zip ^(the node exe ships prebuilt^) and run again.
pause
exit /b 1
)
where powershell >nul 2>nul
if errorlevel 1 (
echo PowerShell was not found. Windows 10 and 11 ship it; nothing else can be done here.
pause
exit /b 1
)
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0start-igneum.ps1"
echo.
echo Igneum has stopped. The log files are next to this file and were uploaded if the upload tool is present.
pause

View file

@ -0,0 +1,55 @@
@echo off
rem Igneum devnet mining test for Windows, the miners only. Double-click this file.
rem It finds the GPUs in this PC, builds the matching GPU workers on first run (NVIDIA: CUDA, AMD: OpenCL), and runs
rem one igneum-miner per worker against a node that is already running (START-NODE.bat here, or the Mac). Logs land next
rem to this file and are uploaded every 60 seconds. START-IGNEUM.bat runs the node and the miners together instead.
rem The window shows a dashboard: hash rate, blocks found and one line per miner. Stop with Ctrl+C; the window stays open at the end.
rem ---- settings: edit these lines only ----------------------------------------------------------
rem auto = a node on this PC (START-NODE.bat, 127.0.0.1) when one is running, else the Mac node at 192.168.68.64. Or an address.
set "NODE_HOST=auto"
set "NODE_PORT=26610"
rem Devnet payout address (igneumdev:...). Leave empty for one address per identity, derived from this PC's name.
set "PAYOUT_ADDRESS="
rem NVIDIA: CUDA device index (0 = first card) and the nvcc architecture (sm_120 = RTX 50 series; "native" lets nvcc pick).
set "NVIDIA_DEVICE=0"
set "CUDA_ARCH=sm_120"
rem AMD: by default the first OpenCL GPU whose vendor string contains AMD_VENDOR is used, never an NVIDIA device.
rem Set AMD_DEVICE to a flat device index from the worker's --list output to override.
set "AMD_VENDOR=Advanced Micro Devices"
set "AMD_DEVICE="
rem Miner identities per GPU vendor. Each identity runs its own worker process (about 1.3 GiB of GPU memory each) with its
rem own vote key and payout address derived from this PC's name and the index, and its own random nonce start.
set "MINERS=8"
rem Set to 0 to skip a vendor even if the hardware is present.
set "USE_NVIDIA=1"
set "USE_AMD=1"
rem Nonces per job (one job = one block template; a multiple of 32). Empty = the miner's default of 16,777,216, which an
rem RTX 5090 identity finishes in under a second. The integrated AMD GPU needs 100 s for that, so it mines stale templates
rem and reports late; 2,097,152 keeps its jobs near 10 s.
set "NVIDIA_JOB_NONCES="
set "AMD_JOB_NONCES=2097152"
rem The window shows a dashboard (one card per GPU, redrawn in place). Set PLAIN=1 for the old scrolling log instead;
rem the launcher also falls back to it by itself when the window is not a real console.
set "PLAIN=0"
rem Visual Studio: the MSVC toolset CUDA 12.8 accepts (14.30 = MSVC v143). Adjust the path if Visual Studio is elsewhere.
set "VCVARS=C:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvarsall.bat"
set "VCVARS_VER=14.30"
rem -------------------------------------------------------------------------------------------------
cd /d "%~dp0"
if not exist "%~dp0start-mining.ps1" (
echo Please extract the whole zip first ^(right-click, Extract All^), then run START-MINING.bat from the extracted folder.
pause
exit /b 1
)
where powershell >nul 2>nul
if errorlevel 1 (
echo PowerShell was not found. Windows 10 and 11 ship it; nothing else can be done here.
pause
exit /b 1
)
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0start-mining.ps1"
echo.
echo The mining test has stopped. The log files are next to this file and were uploaded if the upload tool is present.
pause

View file

@ -0,0 +1,45 @@
@echo off
rem Igneum devnet node for Windows, the node only. Double-click this file.
rem Runs igneumd.exe (the Igneum node) on this PC: the miners mine against it on localhost and it peers with the Mac
rem node. The window shows the NODE card; the node's own output goes to node-<stamp>.log next to this file.
rem Stop with Ctrl+C; the window stays open at the end. START-IGNEUM.bat runs the node and the miners together instead.
rem ---- settings: edit these lines only ----------------------------------------------------------
rem The Mac node (its p2p port). This node dials it at start and keeps retrying; the Mac can dial back as well.
set "MAC_NODE=192.168.68.64:26611"
rem gRPC for the miners on this PC (START-MINING.bat with NODE_HOST=auto looks here first).
set "RPC_LISTEN=127.0.0.1:26610"
rem p2p on all interfaces, so the Mac node can connect in. Windows Firewall asks once: tick Private networks, Allow access.
set "P2P_LISTEN=0.0.0.0:26611"
rem Where the chain database lives.
set "APPDIR=%LOCALAPPDATA%\igneum\devnet"
rem 1 = hand out block templates before this node is synced (the Mac node runs like this because it mines alone).
rem 0 = the miners wait until this node has caught up with the Mac.
set "UNSYNCED_MINING=0"
rem Anything else to pass to igneumd (for example --loglevel=debug).
set "EXTRA_ARGS="
rem Set PLAIN=1 for a scrolling log instead of the dashboard.
set "PLAIN=0"
rem -------------------------------------------------------------------------------------------------
cd /d "%~dp0"
if not exist "%~dp0start-node.ps1" (
echo Please extract the whole zip first ^(right-click, Extract All^), then run START-NODE.bat from the extracted folder.
pause
exit /b 1
)
if not exist "%~dp0igneumd.exe" (
echo igneumd.exe is not in this folder. Extract the whole zip ^(the node exe ships prebuilt^) and run again.
pause
exit /b 1
)
where powershell >nul 2>nul
if errorlevel 1 (
echo PowerShell was not found. Windows 10 and 11 ship it; nothing else can be done here.
pause
exit /b 1
)
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0start-node.ps1"
echo.
echo The node has stopped. The logs are next to this file.
pause

View file

@ -0,0 +1,13 @@
@echo off
rem Stops everything Igneum started on this PC when the launcher window was closed instead of Ctrl+C: the launcher
rem scripts first ^(so nothing restarts^), then the miners and their GPU workers, then the node, then the newest logs
rem are uploaded. Safe to run when nothing is running.
cd /d "%~dp0"
where powershell >nul 2>nul
if errorlevel 1 (
echo PowerShell was not found. Windows 10 and 11 ship it; nothing else can be done here.
pause
exit /b 1
)
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0stop-igneum.ps1"
pause

View file

@ -0,0 +1,26 @@
# Adds the inbound Windows Firewall rule for igneumd.exe (started by ALLOW-FIREWALL.bat). Runs itself again elevated,
# removes any block rule Windows made for the exe (the "Cancel" button on the prompt creates one), then adds an allow
# rule for the exe on the private and domain profiles. 3 October 2026.
param([switch]$Elevated)
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
$exe = Join-Path $root 'igneumd.exe'
$rule = 'Igneum node (igneumd)'
if (-not $Elevated) {
Write-Host "asking for administrator rights to add the firewall rule for $exe"
$psArgs = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', ('"' + $PSCommandPath + '"'), '-Elevated')
try { Start-Process -FilePath powershell.exe -Verb RunAs -Wait -ArgumentList $psArgs } catch { Write-Host "not elevated ($_); nothing changed"; exit 1 }
Write-Host 'done. Start again with START-IGNEUM.bat.'
exit 0
}
try {
$blocked = @(Get-NetFirewallApplicationFilter -ErrorAction SilentlyContinue | Where-Object { $_.Program -and ($_.Program -ieq $exe) } | Get-NetFirewallRule | Where-Object { $_.Action -eq 'Block' })
foreach ($b in $blocked) { Write-Host "removing block rule '$($b.DisplayName)'"; $b | Remove-NetFirewallRule }
Get-NetFirewallRule -DisplayName $rule -ErrorAction SilentlyContinue | Remove-NetFirewallRule
New-NetFirewallRule -DisplayName $rule -Direction Inbound -Program $exe -Action Allow -Profile Private,Domain -Protocol TCP | Out-Null
Write-Host "added inbound allow rule '$rule' for $exe (private and domain networks)"
} catch {
Write-Host "could not change the firewall: $_"
Start-Sleep -Seconds 5
exit 1
}
Start-Sleep -Seconds 2

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,48 @@
#!/usr/bin/env bash
# Builds igneum-windows.zip (the node and the miners in one window on a Windows PC) on the Mac.
# Contents: the launchers and igneum-common.ps1 from this directory, upload-log.bat, the firewall scripts, igneumd.exe
# with its three mingw runtime DLLs, igneum-miner.exe (the one that prints blue= and synced= in `watch`, needed for the
# sync wait), and the worker sources from proto-cuda/ and proto-opencl/. No src.zip: the node exe ships prebuilt.
# Needs the cross-compiled exes in vendor/igneum-node-win/target/x86_64-pc-windows-gnu/release/ (cargo build --release
# --target x86_64-pc-windows-gnu with Homebrew mingw-w64) or the two Windows packages they were shipped in.
# Usage: windows-app/make-package.sh [output zip, default ~/Desktop/igneum-windows.zip]
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(cd "$HERE/../.." && pwd)"
OUT="${1:-$HOME/Desktop/igneum-windows.zip}"
REL="${IGNEUM_WIN_RELEASE:-$ROOT/vendor/igneum-node-win/target/x86_64-pc-windows-gnu/release}"
NODE_EXE="${IGNEUMD_EXE:-$REL/igneumd.exe}"
MINER_EXE="${IGNEUM_MINER_EXE:-$REL/igneum-miner.exe}"
DLL_DIR="${IGNEUM_DLL_DIR:-}" # a directory holding the three DLLs; empty = the Homebrew mingw toolchain
[ -f "$NODE_EXE" ] || { echo "no $NODE_EXE; cross-compile the node first or set IGNEUMD_EXE" >&2; exit 1; }
[ -f "$MINER_EXE" ] || { echo "no $MINER_EXE; cross-compile the miner first or set IGNEUM_MINER_EXE" >&2; exit 1; }
# no grep -q here: pipefail would turn the early exit into a miss (strings gets SIGPIPE)
SYNCED="$(strings -n 6 "$MINER_EXE" | grep -c '^ synced=$' || true)"
if [ "$SYNCED" = "0" ]; then echo "warning: $MINER_EXE does not print synced= in watch; the sync wait falls back to blocks >= headers with a peer" >&2; fi
STAGE="$(mktemp -d)/igneum-windows"
mkdir -p "$STAGE/proto-cuda/packs" "$STAGE/proto-opencl"
cp "$HERE/START-IGNEUM.bat" "$HERE/start-igneum.ps1" "$HERE/START-NODE.bat" "$HERE/start-node.ps1" \
"$HERE/START-MINING.bat" "$HERE/start-mining.ps1" "$HERE/STOP-IGNEUM.bat" "$HERE/stop-igneum.ps1" \
"$HERE/ALLOW-FIREWALL.bat" "$HERE/allow-firewall.ps1" "$HERE/igneum-common.ps1" "$HERE/upload-log.bat" "$HERE/README.txt" "$STAGE/"
cp "$NODE_EXE" "$STAGE/igneumd.exe"
cp "$MINER_EXE" "$STAGE/igneum-miner.exe"
MINGW=/opt/homebrew/opt/mingw-w64/toolchain-x86_64/x86_64-w64-mingw32
for dll in lib/libstdc++-6.dll lib/libgcc_s_seh-1.dll bin/libwinpthread-1.dll; do
name="$(basename "$dll")"
if [ -n "$DLL_DIR" ] && [ -f "$DLL_DIR/$name" ]; then cp "$DLL_DIR/$name" "$STAGE/"
elif [ -f "$MINGW/$dll" ]; then cp "$MINGW/$dll" "$STAGE/"; x86_64-w64-mingw32-strip "$STAGE/$name" 2>/dev/null || true
else echo "missing $name (set IGNEUM_DLL_DIR to a folder holding the three DLLs)" >&2; exit 1; fi
done
# The worker sources: this repository, or (IGNEUM_WORKER_SRC) a folder with the same proto-cuda/ and proto-opencl/ layout,
# such as an extracted igneum-mine-test package, when the repository's working tree carries unreleased edits.
SRC="${IGNEUM_WORKER_SRC:-$ROOT}"
cp "$SRC/proto-cuda/host.cu" "$SRC/proto-cuda/build.bat" "$SRC/proto-cuda/README.md" "$STAGE/proto-cuda/"
cp "$SRC/proto-opencl/host.c" "$SRC/proto-opencl/build.bat" "$SRC/proto-opencl/README.md" "$SRC/proto-opencl/WAVEFRONT.md" "$STAGE/proto-opencl/"
# CRLF for the files Windows tools read as text
for f in "$STAGE"/*.bat "$STAGE/README.txt" "$STAGE/proto-cuda/build.bat" "$STAGE/proto-opencl/build.bat"; do
perl -pi -e 's/\r?\n/\r\n/' "$f"
done
rm -f "$OUT"
(cd "$(dirname "$STAGE")" && zip -qr "$OUT" "$(basename "$STAGE")")
ls -la "$OUT"
unzip -l "$OUT" | tail -n +4 | awk '{print $1, $4}' | sed '/^ *$/d'

View file

@ -0,0 +1,102 @@
# Igneum for Windows: the node and the miners in one window. Started by START-IGNEUM.bat, which sets the settings as
# environment variables (node: MAC_NODE, RPC_LISTEN, P2P_LISTEN, APPDIR, UNSYNCED_MINING, EXTRA_ARGS; miners:
# PAYOUT_ADDRESS, NVIDIA_DEVICE, CUDA_ARCH, AMD_VENDOR, AMD_DEVICE, MINERS, USE_NVIDIA, USE_AMD, NVIDIA_JOB_NONCES,
# AMD_JOB_NONCES, PLAIN, VCVARS, VCVARS_VER). 3 October 2026. The functions live in igneum-common.ps1.
#
# Order:
# 1. igneumd starts (devnet, database under %LOCALAPPDATA%\igneum\devnet, RPC 127.0.0.1:26610, p2p 0.0.0.0:26611,
# peered to the Mac), unless a node already answers on the RPC port: then that node is used and only watched.
# 2. The GPUs and the tools are listed while the node opens its database.
# 3. The dashboard waits for the node to report synced (one reading every 5 s: "node: syncing, N blocks").
# 4. The pack is exported from the node, the workers are built, MINERS identities per GPU vendor start.
# 5. The loop: readings every 30 s, a status block every 30 s, uploads every 60 s, restarts (seed change, crashes),
# the node restarted after a crash with a random 5 to 60 s delay; the miners are restarted once it is back and
# synced, because their gRPC client never reconnects.
# 6. Ctrl+C: the miners stop first, then the node, then the logs are uploaded one last time.
$launcherName = 'igneum'
. (Join-Path $PSScriptRoot 'igneum-common.ps1')
$script:title = 'Igneum devnet: node and miners'
Set-NodeTarget $rpcHost $rpcPort # the miners always dial the node on this PC
Log "igneum launcher on $machine (run $stamp): node $rpcListen, p2p $p2pListen, Mac $macNode, appdir $appDir, unsynced mining $(if ($unsyncedMining) { 'on' } else { 'off' }), miners $minersPerVendor per vendor"
Log "If Windows Firewall asks about igneumd.exe: tick Private networks and click Allow access (the Mac node dials this PC on port $p2pPort). If you clicked Cancel once, run ALLOW-FIREWALL.bat."
# ---- 1. the node ----------------------------------------------------------------------------------------------------------
$script:node.on = $true
$ports = Test-NodePorts
if ($ports -eq 'external') {
Log "a node already answers on ${rpcHost}:${rpcPort}; using it (it was not started by this window and will not be stopped by it)"
Add-Event "using the node already running on ${rpcHost}:${rpcPort}" 'info'
} elseif ($ports -ne 'free') {
Log $ports
exit 1
} else {
if (-not (Test-Path $nodeExe)) { Log 'igneumd.exe is missing from this folder. Extract the whole zip first.'; exit 1 }
$ver = ''
try { $ver = (& $nodeExe --version 2>&1 | Select-Object -First 1) } catch { $ver = "could not run igneumd.exe: $_" }
Log "node binary: $nodeExe ($ver)"
if (Test-TcpPort $macHost ([int]$macPort) 1500) { Log "the Mac node answers on $macNode" } else { Log "the Mac node at $macNode does not answer right now; igneumd will keep retrying (backoff up to 8 minutes)" }
New-Item -ItemType Directory -Force -Path $appDir | Out-Null
$script:node.managed = $true
Start-Node
}
Enable-KeepAwake 'the node and the miners run'
[Console]::TreatControlCAsInput = $true
$stop = $false
$exitCode = 0
$readyCheckAt = $null
try {
# ---- 2. GPUs and tools, while the node opens its database ------------------------------------------------------------
$plan = Find-Hardware
if ($plan.Count -eq 0) { Add-Event 'no GPU worker can run (a tool is missing, see the launcher log); the node runs alone' 'error' }
if ($plan.Count -gt 0 -and -not (Confirm-Miner)) { $plan = @(); Add-Event 'no igneum-miner.exe; the node runs alone' 'error' }
# ---- 3. wait until the node is synced ----------------------------------------------------------------------------------
Start-Dashboard
if ($plan.Count -gt 0) { $script:phase = 'waiting for the node to sync' }
Log 'running. Press Ctrl+C to stop.'
Start-ChainSample
$noPeerWarned = $false
while (-not $stop) {
Start-Sleep -Milliseconds 500
if (Test-StopKey) { $stop = $true; break }
Invoke-CommonStep
if ($script:node.gaveUp) { break }
if ($plan.Count -eq 0) { continue } # node only: stay in this loop
if ($script:node.synced) { break }
$last = $script:chain.last
if (-not $noPeerWarned -and $last -and $last.peers -eq 0 -and ((Get-Date) - $started).TotalSeconds -gt 90) {
$noPeerWarned = $true
Add-Event "no peer after 90 s: is the Mac node running at $macNode? Did the firewall prompt get Allow access?" 'error'
}
}
# ---- 4. pack, workers, miners -----------------------------------------------------------------------------------------
if (-not $stop -and -not $script:node.gaveUp -and $plan.Count -gt 0) {
if (-not (Initialize-Vendors $plan)) { Add-Event 'no worker could be built (see the launcher log); the node runs alone' 'error'; $script:phase = '' }
else {
Start-AllMiners
$readyCheckAt = (Get-Date).AddSeconds(10)
Upload-Logs # first upload right after the start, so a failed start reaches the intake too
Log 'first upload done'
}
}
# ---- 5. the loop ------------------------------------------------------------------------------------------------------
while (-not $stop) {
Start-Sleep -Milliseconds 500
if (Test-StopKey) { $stop = $true; break }
Update-AllInstances
if ($readyCheckAt -and (Get-Date) -ge $readyCheckAt) { Report-WorkerReady; $readyCheckAt = $null }
Invoke-CommonStep
Update-Miners
if ($script:node.gaveUp) { break }
}
} finally {
if ($script:node.gaveUp) { $exitCode = 1 }
Stop-Everything $(if ($stop) { 'Ctrl+C: stopping' } else { '' })
}
exit $exitCode

View file

@ -0,0 +1,48 @@
# Igneum devnet mining test, Windows launcher: the miners only. Started by START-MINING.bat, which sets the settings as
# environment variables (NODE_HOST, NODE_PORT, PAYOUT_ADDRESS, NVIDIA_DEVICE, CUDA_ARCH, AMD_VENDOR, AMD_DEVICE,
# USE_NVIDIA, USE_AMD, MINERS, NVIDIA_JOB_NONCES, AMD_JOB_NONCES, PLAIN, VCVARS, VCVARS_VER). 3 October 2026.
# The functions live in igneum-common.ps1; START-IGNEUM.bat runs the same code with the node in the same window.
#
# Order: GPUs and tools, igneum-miner.exe, the node must answer, the pack is exported and the workers built (cached by
# the pack's seeds), MINERS identities per vendor start, then the dashboard loop (status every 30 s, uploads every 60 s,
# restarts on seed change or crash). Ctrl+C stops everything, prints a summary and uploads once more.
$launcherName = 'launcher'
. (Join-Path $PSScriptRoot 'igneum-common.ps1')
$script:title = 'Igneum devnet mining test'
$note = Resolve-NodeHost # NODE_HOST=auto: a node on this PC first, else the Mac
Log "igneum-mine-test launcher $machine node $nodeUrl$note (run $stamp)"
$plan = Find-Hardware
if ($plan.Count -eq 0) { Log 'nothing to run. Fix the missing tool above and start again.'; exit 1 }
if (-not (Confirm-Miner)) { exit 1 }
if (-not (Test-TcpPort $nodeHost ([int]$nodePort) 3000)) { Log "cannot reach the node at ${nodeHost}:${nodePort}. Is the node running and on the same network? (START-IGNEUM.bat runs one on this PC)"; exit 1 }
Log "node reachable at ${nodeHost}:${nodePort}"
if (-not (Initialize-Vendors $plan)) { exit 1 }
Start-AllMiners
Start-Sleep -Seconds 10
Report-WorkerReady
Upload-Logs # first upload right after the start summary, so a failed start reaches the intake too
Log 'first upload done'
Enable-KeepAwake 'mining'
[Console]::TreatControlCAsInput = $true
Log 'running. Press Ctrl+C to stop (the summary prints and the logs are uploaded one last time).'
Start-ChainSample
Start-Dashboard
$stop = $false
try {
while (-not $stop) {
Start-Sleep -Milliseconds 500
if (Test-StopKey) { $stop = $true; break }
Update-AllInstances
Invoke-CommonStep
Update-Miners
}
} finally {
Stop-Everything $(if ($stop) { 'Ctrl+C: stopping' } else { '' })
}

View file

@ -0,0 +1,66 @@
# Igneum devnet node, Windows launcher: the node only. Started by START-NODE.bat, which sets the settings as environment
# variables (MAC_NODE, RPC_LISTEN, P2P_LISTEN, APPDIR, UNSYNCED_MINING, EXTRA_ARGS, PLAIN). 3 October 2026.
# The functions live in igneum-common.ps1; START-IGNEUM.bat runs the same code with the miners in the same window.
#
# Starts igneumd.exe --devnet with --appdir, --rpclisten, --listen, --addpeer=<Mac>, --nodnsseed, --disable-upnp and
# --nologfiles (the node's output goes to node-<stamp>.log, a new segment per start). The window shows the NODE card
# (a reading every 5 s while syncing, then every 30 s), a status line goes to the launcher log every 30 s, the logs are
# uploaded every 60 s, a crash restarts the node after 5 to 60 s, Ctrl+C stops it.
#
# Why --addpeer and not --connect: in igneumd (kaspad/src/daemon.rs) --connect sets the inbound limit to 0 and skips
# outbound peers, so the Mac could never dial this PC. --addpeer keeps the Mac as a permanent peer (retried with a
# backoff of up to 8 minutes, components/connectionmanager/src/lib.rs) and leaves the listener open for the Mac.
$launcherName = 'node-launcher'
. (Join-Path $PSScriptRoot 'igneum-common.ps1')
$script:title = 'Igneum devnet node'
Set-NodeTarget $rpcHost $rpcPort
Log "igneum node launcher on $machine (run $stamp): Mac node $macNode, RPC $rpcListen, p2p $p2pListen, appdir $appDir, unsynced mining $(if ($unsyncedMining) { 'on' } else { 'off' })"
if (-not (Test-Path $nodeExe)) { Log 'igneumd.exe is missing from this folder. Extract the whole zip first.'; exit 1 }
$ver = ''
try { $ver = (& $nodeExe --version 2>&1 | Select-Object -First 1) } catch { $ver = "could not run igneumd.exe: $_" }
Log "node binary: $nodeExe ($ver)"
if (-not (Test-Path $miner)) { Log 'igneum-miner.exe is not in this folder: the card will only say whether the node process is alive' }
$ports = Test-NodePorts
if ($ports -eq 'external') { Log "a node already answers on ${rpcHost}:${rpcPort}. Is another START-NODE or START-IGNEUM window open? Close it first."; exit 1 }
if ($ports -ne 'free') { Log $ports; exit 1 }
if (Test-TcpPort $macHost ([int]$macPort) 1500) { Log "the Mac node answers on $macNode" } else { Log "the Mac node at $macNode does not answer right now; igneumd will keep retrying (backoff up to 8 minutes)" }
Log "If Windows Firewall asks about igneumd.exe: tick Private networks and click Allow access (the Mac node dials this PC on port $p2pPort). If you clicked Cancel once, run ALLOW-FIREWALL.bat."
New-Item -ItemType Directory -Force -Path $appDir | Out-Null
$script:node.on = $true
$script:node.managed = $true
Start-Node
Start-Sleep -Seconds 3
if ($script:node.proc.HasExited) {
Log "igneumd exited at once with code $($script:node.proc.ExitCode). Last lines:"
foreach ($l in (Get-TailLines $script:node.err 8)) { Log " stderr: $l" }
foreach ($l in (Get-TailLines $script:node.log 8)) { Log " stdout: $l" }
Log "Nothing to restart. Fix the cause above (a wrong --appdir, a port in use, a database from an older build that needs deleting: $appDir) and start again."
exit 1
}
foreach ($l in (Get-TailLines $script:node.log 3)) { Log " node: $l" }
Enable-KeepAwake 'the node runs'
[Console]::TreatControlCAsInput = $true
Log "running. Press Ctrl+C to stop. The node's full output is in $($script:node.log)"
Start-ChainSample
Start-Dashboard
$stop = $false
$exitCode = 0
try {
while (-not $stop) {
Start-Sleep -Milliseconds 500
if (Test-StopKey) { $stop = $true; break }
Invoke-CommonStep
if ($script:node.gaveUp) { break }
}
} finally {
if ($script:node.gaveUp) { $exitCode = 1 }
Stop-Everything $(if ($stop) { 'Ctrl+C: stopping' } else { '' })
}
exit $exitCode

View file

@ -0,0 +1,54 @@
# Stops everything the Igneum launchers started, in order, for the case where the launcher window was closed instead of
# Ctrl+C (started by STOP-IGNEUM.bat). 3 October 2026.
# 1. The launcher scripts themselves (start-igneum.ps1, start-mining.ps1, start-node.ps1), so nothing gets restarted.
# 2. The miners and their GPU workers.
# 3. The node.
# 4. The newest launcher log and node log are uploaded with upload-log.bat when it is present.
$ProgressPreference = 'SilentlyContinue'
$ErrorActionPreference = 'Continue'
$root = $PSScriptRoot
Set-Location $root
function Stop-ByName([string]$name) {
$procs = @(Get-Process -Name $name -ErrorAction SilentlyContinue)
foreach ($p in $procs) { & taskkill.exe /T /F /PID $p.Id 2>$null | Out-Null }
return $procs.Count
}
$launchers = 0
try {
$mine = $PID
$ps = @(Get-CimInstance Win32_Process -Filter "Name='powershell.exe' OR Name='pwsh.exe'" -ErrorAction SilentlyContinue)
foreach ($p in $ps) {
if ($p.ProcessId -ne $mine -and $p.CommandLine -and $p.CommandLine -match 'start-(igneum|mining|node)\.ps1') {
& taskkill.exe /F /PID $p.ProcessId 2>$null | Out-Null
$launchers++
}
}
} catch { }
Write-Host "launcher windows stopped: $launchers"
$miners = Stop-ByName 'igneum-miner'
$workers = (Stop-ByName 'igneum-bench-cuda-devnet') + (Stop-ByName 'igneum-bench-cl-devnet')
Write-Host "miners stopped: $miners, GPU workers stopped: $workers"
Start-Sleep -Seconds 2
$nodes = Stop-ByName 'igneumd'
Write-Host "node processes stopped: $nodes"
$uploader = Join-Path $root 'upload-log.bat'
if (Test-Path $uploader) {
$machine = $env:COMPUTERNAME
$newest = Get-ChildItem -Path $root -Filter '*.log' -ErrorAction SilentlyContinue | Where-Object { $_.Name -match '^(igneum|launcher|node-launcher)-[0-9]{8}-[0-9]{6}\.log$' } | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if ($newest) {
$runStamp = $newest.BaseName.Substring($newest.BaseName.Length - 15) # yyyyMMdd-HHmmss
$label = $newest.BaseName.Substring(0, $newest.BaseName.Length - 16) + "-$machine" # igneum-<PC>, launcher-<PC>, node-launcher-<PC>
$o = cmd /s /c "`"$uploader`" `"$($newest.FullName)`" $label $label-$runStamp" 2>&1
Write-Host ("uploaded " + $newest.Name + ": " + ($o -join ' '))
}
$nodeLog = Get-ChildItem -Path $root -Filter 'node-*.log' -ErrorAction SilentlyContinue | Where-Object { $_.Name -notmatch '^node-launcher' } | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if ($nodeLog) {
$o = cmd /s /c "`"$uploader`" `"$($nodeLog.FullName)`" nodelog-$machine nodelog-$machine-stop" 2>&1
Write-Host ("uploaded " + $nodeLog.Name + ": " + ($o -join ' '))
}
} else { Write-Host 'no upload-log.bat here; the logs stay local' }
Write-Host 'done. Start again with START-IGNEUM.bat.'

View file

@ -0,0 +1,41 @@
@echo off
rem Igneum miner log uploader. Sends the last 256 KB of a log file to the Igneum log intake
rem so Claude on the Mac can read it (node tools/logs.mjs). Needs Windows 10 or 11 (curl.exe, PowerShell).
rem Usage: upload-log.bat <logfile> <label> [run_id]
rem run_id falls back to the IGNEUM_RUN_ID environment variable, then to <label>-<date>-<time>.
rem The key below only authorises log uploads. It is meant to ship inside this package.
setlocal
set "IGNEUM_LOG_URL=https://igneum-six.vercel.app/api/log"
set "IGNEUM_LOG_KEY=***INTAKE-KEY-REMOVED***"
if "%~1"=="" goto usage
if "%~2"=="" goto usage
if not exist "%~1" (
echo upload-log: file not found: %~1
exit /b 2
)
set "LOGFILE=%~f1"
set "LABEL=%~2"
set "RUNID=%~3"
if "%RUNID%"=="" set "RUNID=%IGNEUM_RUN_ID%"
set "OUT=%TEMP%\igneum-upload-%RANDOM%.json"
powershell -NoProfile -ExecutionPolicy Bypass -Command "$ErrorActionPreference='Stop'; $fs=New-Object IO.FileStream($env:LOGFILE,[IO.FileMode]::Open,[IO.FileAccess]::Read,[IO.FileShare]::ReadWrite); $b=New-Object byte[] $fs.Length; [void]$fs.Read($b,0,$b.Length); $fs.Close(); $n=[Math]::Min($b.Length,262144); $t=[Text.Encoding]::UTF8.GetString($b,$b.Length-$n,$n); $r=$env:RUNID; if (-not $r) { $r=$env:LABEL + '-' + (Get-Date -Format 'yyyyMMdd-HHmm') }; $o=@{label=$env:LABEL;machine=$env:COMPUTERNAME;run_id=$r;lines=$t}; [IO.File]::WriteAllText($env:OUT,(ConvertTo-Json $o -Compress),(New-Object Text.UTF8Encoding $false)); Write-Host ('upload-log: run_id ' + $r + ', ' + $n + ' bytes')"
if errorlevel 1 (
echo upload-log: could not read or encode %LOGFILE%
exit /b 3
)
curl.exe -sS --max-time 60 -X POST "%IGNEUM_LOG_URL%" -H "Content-Type: application/json" -H "x-igneum-key: %IGNEUM_LOG_KEY%" --data-binary "@%OUT%"
set "RC=%ERRORLEVEL%"
echo.
del "%OUT%" >nul 2>&1
if not "%RC%"=="0" (
echo upload-log: curl failed with code %RC%
exit /b %RC%
)
exit /b 0
:usage
echo Usage: upload-log.bat ^<logfile^> ^<label^> [run_id]
exit /b 1