diff --git a/app/igneum-app/Cargo.lock b/app/igneum-app/Cargo.lock
index d0e2e8f7..16aeb43c 100644
--- a/app/igneum-app/Cargo.lock
+++ b/app/igneum-app/Cargo.lock
@@ -219,7 +219,7 @@ dependencies = [
[[package]]
name = "igneum-app"
-version = "0.3.6"
+version = "0.3.7"
dependencies = [
"ed25519-dalek",
"getrandom",
diff --git a/app/igneum-app/Cargo.toml b/app/igneum-app/Cargo.toml
index 53894ca2..5c6630a8 100644
--- a/app/igneum-app/Cargo.toml
+++ b/app/igneum-app/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "igneum-app"
-version = "0.3.6"
+version = "0.3.7"
edition = "2021"
description = "Igneum Miner engine: supervises the node, the miner and the GPU workers, and serves the dashboard on 127.0.0.1"
license = "MIT"
diff --git a/app/igneum-app/resources/igneum-app.rc b/app/igneum-app/resources/igneum-app.rc
index 36d8212a..154d5524 100644
--- a/app/igneum-app/resources/igneum-app.rc
+++ b/app/igneum-app/resources/igneum-app.rc
@@ -6,8 +6,8 @@
1 ICON "igneum.ico"
1 VERSIONINFO
-FILEVERSION 0,3,6,0
-PRODUCTVERSION 0,3,6,0
+FILEVERSION 0,3,7,0
+PRODUCTVERSION 0,3,7,0
FILEFLAGSMASK 0x3fL
FILEFLAGS 0x0L
FILEOS VOS_NT_WINDOWS32
@@ -20,12 +20,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Igneum"
VALUE "FileDescription", "Igneum Miner engine"
- VALUE "FileVersion", "0.3.6"
+ VALUE "FileVersion", "0.3.7"
VALUE "InternalName", "igneum-app"
VALUE "LegalCopyright", "Igneum contributors"
VALUE "OriginalFilename", "igneum-app.exe"
VALUE "ProductName", "Igneum Miner"
- VALUE "ProductVersion", "0.3.6"
+ VALUE "ProductVersion", "0.3.7"
END
END
BLOCK "VarFileInfo"
diff --git a/app/igneum-app/src/jobbuild.rs b/app/igneum-app/src/jobbuild.rs
index 7fe122d7..ff6e73f8 100644
--- a/app/igneum-app/src/jobbuild.rs
+++ b/app/igneum-app/src/jobbuild.rs
@@ -330,6 +330,17 @@ pub fn build_script(p: &BuildParams, job_id: &str, m: &Manifest, target: &str) -
s.push_str(&format!(" if [ -f \"$CARGO_TARGET_DIR/{rel}/{name}\" ]; then cp -f \"$CARGO_TARGET_DIR/{rel}/{name}\" \"$OUT/{target}/{name}\"; echo \"RESULT {target} {name} $(stat -c %s \"$OUT/{target}/{name}\") bytes sha256 $(sha256sum \"$OUT/{target}/{name}\" | cut -c1-64)\"; else echo \"RESULT {target} {name} missing after the build\"; rc=3; fi\n"));
}
s.push_str("fi\n");
+ if windows && u.bins.iter().any(|b| b == "igneumd") {
+ // the three mingw runtime DLLs from THIS toolchain go next to the exes (5 October 2026, 0.3.6: the PC's
+ // GCC 13 exes shipped with the Mac's GCC 16 libstdc++-6.dll, which lacks five codecvt symbols; the node
+ // did not start on either PC). make-payload.sh takes DLLs next to the exes first.
+ s.push_str("if [ \"$rc\" = 0 ]; then\n");
+ s.push_str(" gccdir=$(dirname \"$(x86_64-w64-mingw32-gcc-posix -print-file-name=libstdc++-6.dll)\")\n");
+ s.push_str(" for dll in \"$gccdir/libstdc++-6.dll\" \"$gccdir/libgcc_s_seh-1.dll\" /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll; do\n");
+ s.push_str(&format!(" if [ -f \"$dll\" ]; then cp -f \"$dll\" \"$OUT/{target}/\"; echo \"RESULT {target} $(basename \"$dll\") $(stat -c %s \"$dll\") bytes sha256 $(sha256sum \"$dll\" | cut -c1-64) from $(dirname \"$dll\")\"; else echo \"RESULT {target} runtime dll missing: $dll\"; rc_all=1; fi\n"));
+ s.push_str(" done\n");
+ s.push_str("fi\n");
+ }
if optional {
s.push_str(&format!("[ \"$rc\" = 0 ] || echo \"RESULT {target} {dir} optional on {target}: not fatal\"\n", dir = u.dir));
} else {
@@ -543,6 +554,10 @@ mod tests {
assert!(lin.contains("cd \"$SRC/app/igneum-app\""));
assert!(lin.contains("optional on linux: not fatal"));
assert!(!lin.contains("--target x86_64-pc-windows-gnu"));
+ // the windows stage ships the runtime DLLs of its own toolchain next to the exes; the linux stage does not
+ let win = build_script(&p, id, &m, "windows");
+ assert!(win.contains("-print-file-name=libstdc++-6.dll") && win.contains("libgcc_s_seh-1.dll") && win.contains("libwinpthread-1.dll"), "{win}");
+ assert!(!lin.contains("libstdc++-6.dll"));
assert!(lin.contains("RESULT linux igneumd $(stat"));
let win = build_script(&p, id, &m, "windows");
assert!(win.contains("--target x86_64-pc-windows-gnu") && win.contains("x86_64-w64-mingw32-gcc-posix") && win.contains("link-arg=-static"));
diff --git a/app/windows/version.h b/app/windows/version.h
index 4eae6d67..4aed77d7 100644
--- a/app/windows/version.h
+++ b/app/windows/version.h
@@ -3,6 +3,6 @@
// packaging/windows/Igneum-Miner.iss when the app version moves. Include guards, not #pragma once: rc.exe reads it too.
#ifndef IGNEUM_HOST_VERSION_H
#define IGNEUM_HOST_VERSION_H
-#define IGNEUM_HOST_VERSION_STR "0.3.6"
-#define IGNEUM_HOST_VERSION_RC 0,3,6,0
+#define IGNEUM_HOST_VERSION_STR "0.3.7"
+#define IGNEUM_HOST_VERSION_RC 0,3,7,0
#endif
diff --git a/packaging/mac/app/Info.plist b/packaging/mac/app/Info.plist
index 1688b88d..890fe96a 100644
--- a/packaging/mac/app/Info.plist
+++ b/packaging/mac/app/Info.plist
@@ -11,7 +11,7 @@
CFBundleVersion
VERSION_STAMP
CFBundleShortVersionString
- 0.3.6
+ 0.3.7
CFBundlePackageType
APPL
CFBundleExecutable
diff --git a/packaging/windows/Igneum-Miner.iss b/packaging/windows/Igneum-Miner.iss
index c28370b8..3855fbaf 100644
--- a/packaging/windows/Igneum-Miner.iss
+++ b/packaging/windows/Igneum-Miner.iss
@@ -9,7 +9,7 @@
#define ArtDir "..\..\brand\icons"
#endif
#ifndef AppVersion
- #define AppVersion "0.3.6"
+ #define AppVersion "0.3.7"
#endif
#define AppName "Igneum Miner"
#define Publisher "Igneum"
diff --git a/packaging/windows/check-runtime-dlls.sh b/packaging/windows/check-runtime-dlls.sh
new file mode 100755
index 00000000..fcce6c1f
--- /dev/null
+++ b/packaging/windows/check-runtime-dlls.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+# Refuses a Windows payload whose exes import a symbol the shipped runtime DLL does not export.
+#
+# packaging/windows/check-runtime-dlls.sh
+#
+# 5 October 2026, Igneum Miner 0.3.6: the PC-built igneumd.exe (GCC 13's mingw) was shipped with libstdc++-6.dll from
+# the Mac's toolchain (GCC 16), which no longer exports five std::codecvt symbols the exe imports
+# (_ZNKSt25__codecvt_utf8_utf16_baseIwE10do_unshiftERiPcS2_RS2_ and four siblings); Windows refused to start the node
+# ("Entry Point Not Found") on both PCs. `-C link-arg=-static` had not removed the import. Rule from here: the DLLs
+# in a payload come from the toolchain that linked the exes, and this check runs before anything is signed
+# (push-inputs.sh) and before the installer is packed (make-payload.sh). Needs x86_64-w64-mingw32-objdump (Homebrew
+# mingw-w64 on the Mac); prints a note and exits 0 where it is missing (the runner), exits 1 on a missing symbol.
+set -euo pipefail
+DIR="${1:?folder with the exes and the DLLs}"
+OBJDUMP="${OBJDUMP:-x86_64-w64-mingw32-objdump}"
+if ! command -v "$OBJDUMP" >/dev/null 2>&1; then echo "runtime DLL check: no $OBJDUMP here, skipped (the Mac's push-inputs.sh ran it before signing)"; exit 0; fi
+# objdump -p prints, under "DLL Name: x.dll", one import per line: " > "; and under
+# "[Ordinal/Name Pointer] Table" one export per line: "[ n] +base[ m] "
+imports_from() { "$OBJDUMP" -p "$1" | awk -v dll="$2" 'index(tolower($0), "dll name: " tolower(dll)) > 0 {f=1; next} /DLL Name:/ {f=0} f && /^\t[0-9a-f]+[ \t]+[^ \t]+[ \t]+[0-9a-f]+[ \t]+[^ \t]+[ \t]*$/ {print $NF}' | sort -u; }
+exports_of() { "$OBJDUMP" -p "$1" | awk '/^\t\[ *[0-9]+\] \+base\[ *[0-9]+\][ \t]+[0-9a-f]+[ \t]+[^ \t]+[ \t]*$/ {print $NF}' | sort -u; }
+bad=0; checked=0
+for exe in "$DIR"/igneumd.exe "$DIR"/igneum-miner.exe; do
+ [ -f "$exe" ] || continue
+ for dll in $("$OBJDUMP" -p "$exe" | awk '/DLL Name:/ {print $3}' | sort -u); do
+ case "$dll" in lib*.dll) ;; *) continue ;; esac
+ if [ ! -f "$DIR/$dll" ]; then echo "runtime DLL check: $(basename "$exe") imports $dll and the payload has no $dll"; bad=1; continue; fi
+ missing="$(comm -23 <(imports_from "$exe" "$dll") <(exports_of "$DIR/$dll") || true)"
+ n_imp="$(imports_from "$exe" "$dll" | wc -l | tr -d ' ')"; n_exp="$(exports_of "$DIR/$dll" | wc -l | tr -d ' ')"
+ checked=$((checked+1))
+ if [ -n "$missing" ]; then
+ echo "runtime DLL check: $(basename "$exe") imports $(echo "$missing" | wc -l | tr -d ' ') symbol(s) from $dll that this $dll does not export ($n_imp imported, $n_exp exported):"
+ echo "$missing" | head -5 | sed 's/^/ /'; bad=1
+ else
+ echo "runtime DLL check: $(basename "$exe") <- $dll: all $n_imp imported symbols exported ($n_exp exports)"
+ fi
+ done
+done
+[ "$checked" -gt 0 ] || echo "runtime DLL check: no lib*.dll import in the exes (statically linked)"
+[ "$bad" = 0 ] || { echo "runtime DLL check FAILED: ship the DLLs of the toolchain that linked the exes" >&2; exit 1; }
diff --git a/packaging/windows/make-payload.sh b/packaging/windows/make-payload.sh
index f2f55091..ff1c8400 100755
--- a/packaging/windows/make-payload.sh
+++ b/packaging/windows/make-payload.sh
@@ -55,6 +55,7 @@ for dll in lib/libstdc++-6.dll lib/libgcc_s_seh-1.dll bin/libwinpthread-1.dll; d
elif [ -f "$MINGW/$dll" ]; then cp "$MINGW/$dll" "$STAGE/"; x86_64-w64-mingw32-strip "$STAGE/$name" 2>/dev/null || true
else echo "note: $name not found in the mingw toolchain (the node exe is linked -static, so it may not need it)"; fi
done
+"$(dirname "$0")/check-runtime-dlls.sh" "$STAGE" # every imported libstdc++ symbol exported by the shipped DLL (5 October 2026)
# the prebuilt one-click workers, from where proto-cuda/windows-app/make-package.sh takes them: the NVRTC CUDA worker
# with NVIDIA's redistributable DLLs and licence texts, and the OpenCL worker (IGNEUM_WORKERS_DIR overrides with a flat folder)
diff --git a/packaging/windows/push-inputs.sh b/packaging/windows/push-inputs.sh
index 3676044a..05f515dc 100755
--- a/packaging/windows/push-inputs.sh
+++ b/packaging/windows/push-inputs.sh
@@ -43,11 +43,16 @@ TOKEN="$(tr -d '[:space:]' < "$TOKEN_FILE")"
STAGE="$(mktemp -d)/payload-inputs"
mkdir -p "$STAGE"
cp "$REL/igneumd.exe" "$REL/igneum-miner.exe" "$STAGE/"
+# the runtime DLLs: the ones next to the exes first (a PC build job ships its own toolchain's, 0.3.7), else this Mac's
+# toolchain (which linked a Mac cross-build); then the gate: every symbol the exes import must be exported by the DLL
+# (5 October 2026, 0.3.6: GCC 13 exes with the GCC 16 DLL, "Entry Point Not Found" on both PCs)
for dll in lib/libstdc++-6.dll lib/libgcc_s_seh-1.dll bin/libwinpthread-1.dll; do
name="$(basename "$dll")"
- if [ -f "$MINGW/$dll" ]; then cp "$MINGW/$dll" "$STAGE/"; x86_64-w64-mingw32-strip "$STAGE/$name" 2>/dev/null || true
+ if [ -f "$REL/$name" ]; then cp "$REL/$name" "$STAGE/"; echo "$name: from next to the exes ($REL)"
+ elif [ -f "$MINGW/$dll" ]; then cp "$MINGW/$dll" "$STAGE/"; x86_64-w64-mingw32-strip "$STAGE/$name" 2>/dev/null || true; echo "$name: from the Mac toolchain ($MINGW)"
else echo "note: $name not in the mingw toolchain (the node exe is linked -static, so it may not need it)"; fi
done
+"$(dirname "$0")/check-runtime-dlls.sh" "$STAGE"
if [ -f "$NVRTC_DIR/igneum-worker-cuda.exe" ]; then
cp "$NVRTC_DIR/igneum-worker-cuda.exe" "$STAGE/"
for f in "$NVRTC_DIR"/redist/bin/nvrtc*.dll "$NVRTC_DIR"/redist/LICENSE-*.txt "$NVRTC_DIR/THIRD-PARTY.md"; do [ -f "$f" ] && cp "$f" "$STAGE/"; done
diff --git a/proto-cuda/windows-node/cross-build.sh b/proto-cuda/windows-node/cross-build.sh
index f08328f2..fcd1d9c1 100755
--- a/proto-cuda/windows-node/cross-build.sh
+++ b/proto-cuda/windows-node/cross-build.sh
@@ -27,7 +27,11 @@ export CXX_x86_64_pc_windows_gnu=x86_64-w64-mingw32-g++
export AR_x86_64_pc_windows_gnu=x86_64-w64-mingw32-ar
export LIBCLANG_PATH=/opt/homebrew/opt/llvm/lib
export BINDGEN_EXTRA_CLANG_ARGS_x86_64_pc_windows_gnu="--target=x86_64-w64-mingw32 --sysroot=$MINGW -I$MINGW/include"
-export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUSTFLAGS="-C link-arg=-static -C link-arg=-static-libgcc"
+# 5 October 2026 (0.3.7): -static-libstdc++ added as a try; -static alone left libstdc++-6.dll in the import table of
+# every exe so far (0.3.5's and the PC's), which is why the payload must ship the DLLs of the toolchain that linked
+# the exes (push-inputs.sh takes them from next to the exes first, then from this toolchain) and why push-inputs.sh
+# refuses an exe that imports a symbol the shipped DLL does not export.
+export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUSTFLAGS="-C link-arg=-static -C link-arg=-static-libgcc -C link-arg=-static-libstdc++"
cd "$NODE"
nice -n 19 cargo build --release -j "$JOBS" -p kaspad -p igneum-miner --features igneum-pow --target x86_64-pc-windows-gnu
for exe in igneumd igneum-miner; do
diff --git a/tools/build-job.mjs b/tools/build-job.mjs
index 58c0953a..32ce14c0 100755
--- a/tools/build-job.mjs
+++ b/tools/build-job.mjs
@@ -157,7 +157,7 @@ async function fetchOutputs(id, finals) {
if (o.target === 'linux') { try { chmodSync(plain, 0o755); } catch {} }
let note = '';
if (o.target === 'windows') {
- const pe = peCheck(plain);
+ const pe = peCheck(plain, o.name.endsWith('.dll') ? 16 * 1024 : undefined); // the runtime DLLs the PC ships next to the exes are small
if (!pe.ok) { console.log(`${o.name}: PE check FAILED: ${pe.problems.join('; ')}`); bad++; continue; }
note = ` PE ok (${pe.sections.join(' ')}, subsystem ${pe.subsystem})`;
if (o.name === 'igneum-app.exe') {