The Notices block takes the hot-plug notices as on gpu-hotplug (card added, not usable, removed). The View block
and the Mine rows, the first-run rows and the Settings cards show a removed card (dimmed, no switch, the row goes
after five minutes) and a faulty card (named in ember, the OS problem code, the reboot hint, no switch); the big
button and the counts take only present cards; the name tooltip carries the tool's code, the device, the platform
and the PCI address. view.test.mjs covers the two states. host.cpp keeps both the WM_GETMINMAXINFO and the
WM_DEVICECHANGE cases.
Build tooling: push-build-inputs.sh and build-job.mjs take --no-node (the app engine only, no node source, no node
build, no node tests), for an app-only PC compile.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The one long page becomes a rail with six sections, a thin top bar and the status strip under it; the setup
screens and the key sheet stay. Mine: one big start/stop, the numbers, one row per GPU with its switch, hash
rate, temperature and power. Prove: the switch with plain words, the counts, the verifier, the pinned ids.
Rewards: the address with one Copy, the key backup card, another address. Node: the plain lines, the consensus
digest, the next switch. Updates: the version, the jobs. Settings: one switch or slider per setting with one line
of help. Every function that existed is placed, none removed.
Engine (three small additions, each with a unit test): node.consensus_digest from the node's own line,
node.consensus_switches from the override file, proving.program_id and aggregator_id from the host's --mode id.
Hosts: the window minimum is 900 x 600 on both. UI tests: view.test.mjs (10) joins notices and update-card in CI.
Proof: docs/plans/miner-ui-2.md and the 19 screenshots under docs/plans/miner-ui-2/, taken from a build of this
tree running on its own port against the devnet.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
5 October 2026: an RX 9070 XT went into PC 1 through a Sonnet USB4 box while the app ran and nothing noticed; the
app detected cards once at start. Now src/hotplug.rs compares every enumeration with the list (key, else vendor +
name when unique; a card whose tool did not answer is never called removed): a new usable card starts a worker,
enabled by default like a card at start, with "New card: <name>, mining" on the strip and in the log; a card
Windows lists with a problem code (Win32_VideoController Status / ConfigManagerErrorCode) is shown as "<name>: not
usable (Code 43)" with the reboot-or-reinstall hint and no worker; a card that disappears has its worker stopped
(quit, 8 s) and its row says removed for five minutes, then hides; an unchanged list touches nothing. The Windows
host sends "detect" on WM_DEVICECHANGE; the engine polls every 60 s (300 s on macOS, no GPU hot-plug there).
detect.rs: the Ryzen iGPU is "gfx1036" to the OpenCL worker, so the APU gfx codes count as integrated, plus the
adapter row's Intel processor string and a dedicated memory under 1 GB; integrated defaults to off with "integrated
GPU, off by default (2 to 3 MH/s for 30 W)" on the row, and the user's choice is kept across re-detections and
restarts (settings, found by key or by vendor + name when the index moved).
Console: the engine logs "cards: <name> [<kind>, <state>] | ..." at start, on every change and every 10 minutes;
relay/lib/parse.mjs reads it and the hot-plug events, the machines API and tools/console.mjs machines show them.
Tests: hotplug.rs (added, removed, moved, errored, recovered, revived, unchanged, twins, user override kept,
the console line), detect.rs (PC 1's adapter lines, the Mac, kind classification, the unusable row),
notices.test.mjs (card notices), relay parse.test.mjs (cards line). cargo test -p igneum-app: 91 passed.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The lock screen (ui/lock-screen.js, pure, with lock-screen.test.mjs): the coin large on the ember glow, the name in
Unbounded, the short address in mono, one control. Touch ID enrolled in the app window: the fingerprint button, its
line, a quiet "Use password" that reveals the field in place (the control area keeps one height). Otherwise the
password field is the control. Locking is a 250 ms transition from the home screen, not a cut. The glow breathes;
reduced motion stops it.
No automatic sheet: it appears on a tap, Return or Space on the button (focused on arrival and when the window
comes back). One exception: the first arrival after the person opened the app, 600 ms after the lock screen is
drawn, when "Ask for Touch ID when the wallet opens" is on (new setting, default on). Never on an idle lock, a hand
lock, the window coming back, after a cancelled sheet ("Touch ID cancelled, tap to try again", no modal), or on the
updater's relaunch (updated_from set). Escape in the password field returns to the button.
The idle lock: Settings > Lock when idle, 1, 5 (default), 15, 60 minutes or never (settings.json idle_lock_min;
idle_lock mirrors on/off for 0.1.2 and 0.1.3; /api/settings takes idle_lock_min and ask_on_open and refuses other
minutes). The engine's lock event names the minutes.
Also: the three wallet switches in Settings were invisible (the miner's .switch hid the input behind a .track the
wallet never renders); the box shows now. ?bio=touch shows the Touch ID layout without a host, for screenshots.
Tests: node --test ui/lock-screen.test.mjs (5) + update-card.test.mjs (4); cargo test in app/igneum-wallet, 18
passed (the settings migration test is new). Verified in the browser pane at 900x700 and 1280x800 against a scratch
engine. Shipped: Igneum-Wallet-0.1.4.dmg published to the new download folder and bridged into the old one (the
installed 0.1.3 polls the old folder; rotation phase 2 had removed the wallet manifest there); the project lead's wallet went
0.1.3 -> 0.1.4 in 28 s (check 15:48:50Z, 0.1.4 up 15:49:19Z), no sheet on the relaunch.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
0.3.6 on both PCs: igneumd.exe (built on PC 1 with GCC 13's mingw) shipped with the Mac toolchain's GCC 16
libstdc++-6.dll, which no longer exports seven symbols the exe imports (std::codecvt_utf8_utf16 and a
stringbuf::seekpos); Windows refused the node with Entry Point Not Found. -C link-arg=-static had never removed
the libstdc++ import (0.3.5's Mac-built exe carries it too).
- packaging/windows/check-runtime-dlls.sh: objdump imports per DLL against the DLL's exports; shown to fail
the 0.3.6 pairing (7 missing) and pass 0.3.5's; run by push-inputs.sh before signing and by make-payload.sh
- push-inputs.sh: DLLs next to the exes first, then the Mac toolchain
- jobbuild.rs: the PC's windows stage copies its own toolchain's three DLLs into the pack (unit test);
build-job.mjs accepts the small DLL PE files and places them next to the exes
- cross-build.sh: -static-libstdc++ added as a try (measured on the 0.3.7 build)
- the six version files: 0.3.7
The window host owns the prompt and the secret (app/mac/Biometric.swift): LAPolicy.deviceOwnerAuthenticationWithBiometrics
with "Use password" as the fallback button (never the device password), the wallet's password sealed to a Secure
Enclave key made with .biometryCurrentSet (the Keychain refuses biometric access controls under the ad hoc signature,
-34018, measured) in <data>/wallet/biometric.json; a fingerprint change invalidates it. The engine owns the gate
(igneum-common/src/biometric.rs): a nonce per action, read by the host with its token (the HOST line on stdout,
X-Igneum-Host on host-only calls), confirmed after the prompt, taken once within 30 s and bound to the exact quote;
/api/send refuses without it while enrolled; /api/reveal with a nonce reads the unlocked key in memory; the password
never goes through the page (enrolment parks it under a one-time token the host takes). Idle lock after 5 minutes
without window activity (setting, default on). A password change or a wallet removal deletes the sealed file.
Reason lines in our voice ("Unlock your wallet", "Send 1.5 IGN to 0x7E5F…5Bdf", "Show your recovery words"); the page
shows its own ember line after every prompt. Windows: app/windows/biometric.h (UserConsentVerifier through
IUserConsentVerifierInterop, DPAPI), wired into wallet-host.cpp and BUILD-WALLET-APP.bat, not yet compiled on a PC.
Hosts gain a @main entry so Biometric.swift compiles alongside; build-wallet-dmg.sh links LocalAuthentication.
Balance card: the coin at 56 px, "0" (or the balance) as soon as the node answers, "reading the chain, N of M blocks"
under it while the history scans. Version: v0.1.2 in the brand band, "Igneum Wallet 0.1.2 · up to date" in Settings.
Unit tests: the gate (7, igneum-common), the wallet's 17 still green. README: the flows, the threat model, what was
verified on this Mac (enrol and unlock through the real prompt) and what was not.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
app/igneum-common (new library crate): the platform helpers with the app identity as a parameter, the payout key
code, the signed OTA manifest (byte-identical to the miner's), the manifest fetch and download check, the 127.0.0.1
server primitives and a JSON client, run_timeout, the packaged config and machine id. Nothing in app/igneum-app
changed; `cargo check -p igneum-app` still passes.
app/igneum-wallet (new): create (24 words, three typed back) or import (words, raw key, the miner's wallet.json),
sealed with Argon2id + XChaCha20-Poly1305 under the user's password; balance, send (EIP-1559, signed in Rust, zero
address refused, fee shown as base fee + tip), receive with a QR drawn locally, history (transfers, block rewards from
the execution records, shard payouts when they exist); finality per transaction verified by the wallet itself with the
node's own finality code (certificate from the blocks after the checkpoint, canonical voter list, aggregate BLS
signature, 2/3 of active and of total weight), shown as pending / in a block / final with the checkpoint index; export
to MetaMask (key with a warning, network parameters, add-network link); node source order: the miner app's node,
the environment's node, the bundled igneumd, the packaged public RPC. 13 unit tests.
Hosts and packaging: app/mac/IgneumWallet.swift, app/windows/wallet-host.* (untested), packaging/mac/build-wallet-dmg.sh,
packaging/windows/Igneum-Wallet.iss, publish-manifest.sh --product wallet (miner path unchanged).
tools/wallet-testnet/run.mjs and the bench-log entry: on igneum-devnet-958 a transfer went pending, in a block and
final under checkpoint 5, 170.6 s after sending, the certificate verified by the wallet.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
windows.yml: parse job (every .ps1 through the Windows PowerShell 5.1 parser with a negative fixture, PSScriptAnalyzer
as warnings, parenthesis check of every .bat/.cmd with a negative fixture), build job (engine on the MSVC target,
window host through BUILD-APP.bat as it is, payload through make-payload.sh in Git Bash, installer through
build-installer.ps1, smoke run of both exes, launcher DRY_RUN, three artifacts for 90 days).
push-inputs.sh publishes payload-inputs.zip (node, miner, workers, NVRTC DLLs) to the downloads host from the Mac;
fetch-ci-artifacts.sh pulls the green run's installer and payload back into the downloads folder.
Host: --version and --help, version.h shared with host.rc. Launcher: DRY_RUN=1 prints the plan and starts nothing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>