diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c104ba6bd..90af33b10 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,6 +91,8 @@ jobs: run: bash tools/ci/prover-socket-check.sh - name: commit-string gate self-test (the empty-commit class of 6 October 2026; the gate itself runs in build-remote.sh, cross-remote.sh and cross-build.sh on every node binary) run: bash tools/ci/commit-string-check.sh --self-test + - name: build server remote checkout self-test (the stale-overlay class of 6 October 2026) + run: bash infra/build-server/remote-run.sh --self-test - name: no secret file names and no 64-hex secrets in the tree (self-test first, then the tree) run: bash tools/ci/no-secrets-check.sh --self-test && bash tools/ci/no-secrets-check.sh - name: faucet unit tests (validation, the daily limits, the signed transaction; keccak, RLP and secp256k1 vectors) diff --git a/docs/plans/build-server.md b/docs/plans/build-server.md index 9e8b78dd1..1b1cc697d 100644 --- a/docs/plans/build-server.md +++ b/docs/plans/build-server.md @@ -83,7 +83,17 @@ Also logged for context: the Mac's Linux cross-build with zig (`infra/cross/buil | R6 | The box is never a node host for the live devnet and never holds a secret (no `~/.config/igneum` there). The Devnet 2 seed on it runs under its own unit with `--devnet --devnet-suffix=` on 26611 (ufw already open) when that work starts. | | R7 | CLAUDE.md line "nothing is built on a server" and "Windows builds go to the GitHub runner, Linux binaries come from infra/cross/build-linux.sh" are rewritten when R1 and R2 are adopted; until then the box is the measured option, not the rule. | -## 5. What the box does not do yet +## 5. Gotchas met on the first day + +| Case | What happened | Fix | +|---|---|---| +| Stale overlay blocks the next checkout (PC 1 worker, 6 Oct 2026) | build-remote.sh rsyncs uncommitted files over the box's checkout; on the next commit `git checkout -B` refused with "local changes would be overwritten" | remote-run.sh `checkout` mode: `git checkout -- .` and `git clean -fd` (target dirs, sha stamps and ignored files kept) before the branch checkout, then the overlay; `remote-run.sh --self-test` reproduces the dirty tree and shows the mode landing on the new commit clean | +| An all-identical overlay listed only directories | the first run's touch pipeline got an empty file list and failed | files only are counted and re-stamped (lib.sh bs_overlay_dir) | +| bash 3.2 on the Mac treats an empty array as unbound under `set -u` | run-from-mac.sh died on `PASS[*]` | a string instead of an array | +| `grep -q` plus `pipefail` turned a strings hit into a miss | the commit-string gate failed a stamped igneumd on its first use (SIGPIPE on `strings`) | `grep -c` | +| Let's Encrypt saw NXDOMAIN for build.igneum.network | the deSEC record was minutes old; Ubuntu's Caddy then fell back to ZeroSSL and failed with HTTP 422 for ever | issuer pinned to Let's Encrypt; the retry got the certificate | + +## 6. What the box does not do yet | Gap | Why it matters | Next step | |---|---|---| diff --git a/docs/plans/site-miner-2026-10-06.png b/docs/plans/site-miner-2026-10-06.png new file mode 100644 index 000000000..fa73d0e66 Binary files /dev/null and b/docs/plans/site-miner-2026-10-06.png differ diff --git a/docs/plans/site-ui-3-shots/item0/index-1440-dark-clip.png b/docs/plans/site-ui-3-shots/item0/index-1440-dark-clip.png new file mode 100644 index 000000000..7685b15c8 Binary files /dev/null and b/docs/plans/site-ui-3-shots/item0/index-1440-dark-clip.png differ diff --git a/docs/plans/site-ui-3-shots/item0/index-1440-dark-fail-clip.png b/docs/plans/site-ui-3-shots/item0/index-1440-dark-fail-clip.png new file mode 100644 index 000000000..735c57d64 Binary files /dev/null and b/docs/plans/site-ui-3-shots/item0/index-1440-dark-fail-clip.png differ diff --git a/docs/plans/site-ui-3-shots/item0/index-1440-dark-fail.jpg b/docs/plans/site-ui-3-shots/item0/index-1440-dark-fail.jpg new file mode 100644 index 000000000..d3f6d0f35 Binary files /dev/null and b/docs/plans/site-ui-3-shots/item0/index-1440-dark-fail.jpg differ diff --git a/docs/plans/site-ui-3-shots/item0/index-1440-dark.jpg b/docs/plans/site-ui-3-shots/item0/index-1440-dark.jpg new file mode 100644 index 000000000..96e03e66e Binary files /dev/null and b/docs/plans/site-ui-3-shots/item0/index-1440-dark.jpg differ diff --git a/docs/plans/site-ui-3-shots/item0/index-390-dark-clip.png b/docs/plans/site-ui-3-shots/item0/index-390-dark-clip.png new file mode 100644 index 000000000..fb4300f80 Binary files /dev/null and b/docs/plans/site-ui-3-shots/item0/index-390-dark-clip.png differ diff --git a/docs/plans/site-ui-3-shots/item0/index-390-dark.jpg b/docs/plans/site-ui-3-shots/item0/index-390-dark.jpg new file mode 100644 index 000000000..b32bc0ceb Binary files /dev/null and b/docs/plans/site-ui-3-shots/item0/index-390-dark.jpg differ diff --git a/infra/build-server/lib.sh b/infra/build-server/lib.sh index b0dcfbd9e..b16fb76c0 100755 --- a/infra/build-server/lib.sh +++ b/infra/build-server/lib.sh @@ -111,10 +111,13 @@ bs_push_and_checkout() { [ "$BS_TOP_REL" = . ] && remote_top="$BS_REMOTE_WT" bs_log "push $BS_TOP HEAD $BS_SHA ($BS_BRANCH) -> $url" GIT_SSH_COMMAND="$BS_SSH_CMD" git -C "$BS_TOP" push -q --force "$url" "HEAD:refs/heads/$BS_BRANCH" || bs_die "push to the mirror failed" - bs_ssh "set -e; mkdir -p '$BS_REMOTE_WT' - if [ ! -d '$remote_top/.git' ]; then rm -rf '$remote_top'; git clone -q --no-checkout '$BS_MIRROR' '$remote_top'; fi - cd '$remote_top'; git fetch -q origin '+refs/heads/*:refs/remotes/origin/*'; git checkout -q -B '$BS_BRANCH' '$BS_SHA'; git reset -q --hard '$BS_SHA' - git status --porcelain | head -3" || bs_die "remote checkout at $remote_top failed" + # the checkout runs as remote-run.sh's `checkout` mode: discard the previous overlay (tracked edits and untracked files, + # target dirs kept), then the branch at the commit. 6 October 2026, PC 1 worker's first use: the overlay of an earlier + # commit's uncommitted files stayed in the box's tree and `git checkout -B` refused with "local changes would be overwritten". + BR_MODE=checkout BR_CO_DIR="$remote_top" BR_CO_MIRROR="$BS_MIRROR" BR_CO_BRANCH="$BS_BRANCH" BR_CO_SHA="$BS_SHA" BR_CO_WT="$BS_REMOTE_WT" \ + bash -c ' + for v in BR_MODE BR_CO_DIR BR_CO_MIRROR BR_CO_BRANCH BR_CO_SHA BR_CO_WT; do printf "export %s=%q\n" "$v" "${!v}"; done + cat "$0"' "$(dirname "${BASH_SOURCE[0]}")/remote-run.sh" | bs_ssh 'bash -s' || bs_die "remote checkout at $remote_top failed" BS_REMOTE_TOP="$remote_top" } diff --git a/infra/build-server/remote-run.sh b/infra/build-server/remote-run.sh index 2ef2bdbd8..a4ddc8b52 100755 --- a/infra/build-server/remote-run.sh +++ b/infra/build-server/remote-run.sh @@ -17,8 +17,63 @@ # 2026): on success, on failure and on the slot give-up. UTC ISO 8601 Z times, numbers unquoted, unknown fields omitted, # artefacts with bytes and sha256 only when the run succeeded (a failed build would list the previous build's files), # the line kept under 4 KB. +# Modes (BR_MODE, default run): `checkout` resets the box's tree and checks the branch out at the commit (lib.sh +# bs_push_and_checkout: BR_CO_DIR, BR_CO_MIRROR, BR_CO_BRANCH, BR_CO_SHA, BR_CO_WT); `--self-test` (first argument) builds a +# scratch mirror and clone, dirties the clone the way a build's overlay does, moves the mirror one commit on, and shows the +# checkout mode lands on the new commit with a clean tree (the 6 October 2026 case: a stale overlay made `git checkout -B` +# refuse with "local changes would be overwritten"). set -uo pipefail -. /etc/profile.d/igneum-build.sh +[ -f /etc/profile.d/igneum-build.sh ] && . /etc/profile.d/igneum-build.sh + +# discard the previous overlay (tracked edits and untracked files; target dirs, the sha stamps and anything ignored are kept), +# fetch, then the branch at the commit. Runs in BR_CO_DIR, clones it from BR_CO_MIRROR when it has no .git. +checkout_tree() { + local dir="$1" mirror="$2" branch="$3" sha="$4" wt="${5:-}" + set -e + [ -n "$wt" ] && mkdir -p "$wt" + if [ ! -d "$dir/.git" ]; then rm -rf "$dir"; git clone -q --no-checkout "$mirror" "$dir"; fi + cd "$dir" + git checkout -q -- . 2>/dev/null || true + git clean -qfd -e target -e 'target-*' -e '.build-remote-sha-*' -e '.cross-remote-sha-*' -e sccache + git fetch -q origin '+refs/heads/*:refs/remotes/origin/*' + git checkout -q -B "$branch" "$sha" + git reset -q --hard "$sha" + # what may remain untracked: target dirs, the sha stamps of build-remote.sh and cross-remote.sh (kept so a build after the + # checkout knows whether to clean kaspa-build-info), sccache; the first live run after this mode was added failed on a + # stamp it had itself kept (6 October 2026, 18:14 UTC: the self-test had no stamp file; it has one now) + local left; left=$(git status --porcelain | grep -vE '^\?\? (target|target-|sccache|\.build-remote-sha-|\.cross-remote-sha-)' | head -3 || true) + [ -z "$left" ] || { echo "checkout: tree not clean after reset at $dir: $left" >&2; return 1; } + set +e +} + +if [ "${1:-}" = --self-test ]; then + t=$(mktemp -d); trap 'rm -rf "$t"' EXIT + git init -q --bare -b master "$t/mirror.git" + git init -q -b master "$t/src"; git -C "$t/src" -c user.name=t -c user.email=t@t commit -q --allow-empty -m one + echo a > "$t/src/a.txt"; git -C "$t/src" add a.txt; git -C "$t/src" -c user.name=t -c user.email=t@t commit -q -m two + git -C "$t/src" push -q "$t/mirror.git" master + sha1=$(git -C "$t/src" rev-parse HEAD) + checkout_tree "$t/box" "$t/mirror.git" master "$sha1" || { echo "self-test: first checkout failed"; exit 1; } + # the overlay of a build: a tracked file edited, an untracked file added, a target dir that must survive + echo edited > "$t/box/a.txt"; echo new > "$t/box/b.txt"; mkdir -p "$t/box/target/release"; echo bin > "$t/box/target/release/x"; echo "$sha1" > "$t/box/.build-remote-sha-target" + # the Mac moves on: a new commit that changes a.txt + echo a2 > "$t/src/a.txt"; git -C "$t/src" -c user.name=t -c user.email=t@t commit -qam three; git -C "$t/src" push -q "$t/mirror.git" master + sha2=$(git -C "$t/src" rev-parse HEAD) + if (cd "$t/box" && git fetch -q origin && git checkout -q -B master "$sha2" 2>/dev/null); then echo "self-test: the plain checkout did NOT refuse on the dirty tree (the case no longer reproduces; the reset is still right)"; else echo "self-test: the plain checkout refuses on the dirty tree, as on 6 October"; fi + checkout_tree "$t/box" "$t/mirror.git" master "$sha2" || { echo "self-test: checkout mode FAILED on the dirty tree"; exit 1; } + [ "$(git -C "$t/box" rev-parse HEAD)" = "$sha2" ] || { echo "self-test: wrong commit"; exit 1; } + [ "$(cat "$t/box/a.txt")" = a2 ] || { echo "self-test: tracked edit survived"; exit 1; } + [ ! -e "$t/box/b.txt" ] || { echo "self-test: untracked overlay file survived"; exit 1; } + [ -f "$t/box/target/release/x" ] || { echo "self-test: target dir was cleaned"; exit 1; } + [ -f "$t/box/.build-remote-sha-target" ] || { echo "self-test: the sha stamp was cleaned"; exit 1; } + echo "self-test: checkout mode lands on the new commit with a clean tree, target dir and sha stamp kept"; exit 0 +fi + +if [ "${BR_MODE:-run}" = checkout ]; then + : "${BR_CO_DIR:?}" "${BR_CO_MIRROR:?}" "${BR_CO_BRANCH:?}" "${BR_CO_SHA:?}" + checkout_tree "$BR_CO_DIR" "$BR_CO_MIRROR" "$BR_CO_BRANCH" "$BR_CO_SHA" "${BR_CO_WT:-}"; exit $? +fi + : "${BR_DIR:?}" "${BR_CMD:?}" "${BR_LABEL:?}" "${BR_TOOL:?}" "${BR_KIND:?}" BR_HOST=$(hostname); BR_PID=$$; BR_T0=$(date +%s) export BR_HOST BR_PID BR_T0 diff --git a/relay/playbooks/site-capture-pc1.ps1 b/relay/playbooks/site-capture-pc1.ps1 new file mode 100644 index 000000000..a88122230 --- /dev/null +++ b/relay/playbooks/site-capture-pc1.ps1 @@ -0,0 +1,62 @@ +# Site capture on PC 1 (6 October 2026): screenshots of the installed Igneum Miner's own UI for igneum.network/miner. +# A read-only run job. It reads the installed app's URL file and GETs the four pages with Edge headless, which every +# Windows box has. Nothing is posted to the app: no quit, no pause, no resume, no settings change; the miners keep +# running; Edge runs as its own process on a scratch profile under the job folder and exits after each shot. +# Each PNG goes back as a raw file drop on the relay (under 4 MB each; x-igneum-key is the job's intake key, which +# may report but never execute). The Mac reads them with tools/relay.mjs and masks the rail foot and the machine id +# before anything reaches site/img. Every line that matters starts with RESULT. +$ErrorActionPreference = 'Continue' +function Say($t) { Write-Output "RESULT $t" } + +$urlFile = @((Join-Path $env:IGNEUM_APP_DATA 'app\app.url'), (Join-Path $env:LOCALAPPDATA 'igneum\app\app.url')) | Where-Object { Test-Path $_ } | Select-Object -First 1 +if (-not $urlFile) { Say 'no app.url found: the installed app is not running'; exit 2 } +$base = (Get-Content -LiteralPath $urlFile -Raw).Trim() +if (-not $base.EndsWith('/')) { $base += '/' } +Say 'app url read (the installed app; read only)' + +$edge = @("${env:ProgramFiles(x86)}\Microsoft\Edge\Application\msedge.exe", "$env:ProgramFiles\Microsoft\Edge\Application\msedge.exe") | Where-Object { Test-Path $_ } | Select-Object -First 1 +if (-not $edge) { Say 'msedge.exe not found'; exit 2 } +Say ("edge " + (Get-Item $edge).VersionInfo.ProductVersion) + +try { + $state = Invoke-RestMethod -Uri ($base + 'api/state') -TimeoutSec 10 + $cards = @($state.mining.cards) + Say ("state: app {0}, mining {1}, {2} card(s), {3:N1} MH/s total" -f $env:IGNEUM_APP_VERSION, $state.mining.state, $cards.Count, [double]$state.mining.hash_total) + foreach ($c in $cards) { Say ("card {0}: {1}, {2:N1} MH/s, {3} W, sweep {4}" -f $c.name, $c.state, [double]$c.hash_now, $c.power_w, $c.sweep_state) } +} catch { Say ("api/state not read: " + $_.Exception.Message) } + +$out = Join-Path $env:IGNEUM_JOB_DIR 'shots' +New-Item -ItemType Directory -Force -Path $out | Out-Null +$profileDir = Join-Path $env:IGNEUM_JOB_DIR 'edge-profile' + +$shots = @() +foreach ($theme in @('dark', 'light')) { + foreach ($page in @('mine', 'earnings', 'prove', 'settings')) { $shots += @{ name = "pc1-$page-$theme"; page = $page; w = 1440; h = 900; dark = ($theme -eq 'dark') } } + $shots += @{ name = "pc1-mine-phone-$theme"; page = 'mine'; w = 390; h = 844; dark = ($theme -eq 'dark') } + $shots += @{ name = "pc1-settings-phone-$theme"; page = 'settings'; w = 390; h = 844; dark = ($theme -eq 'dark') } +} + +foreach ($s in $shots) { + $file = Join-Path $out ($s.name + '.png') + $edgeArgs = @('--headless=new', '--disable-gpu', '--hide-scrollbars', '--no-first-run', '--disable-extensions', '--disable-features=WebContentsForceDark', + "--user-data-dir=$profileDir", '--force-device-scale-factor=2', '--virtual-time-budget=10000', ('--window-size=' + $s.w + ',' + $s.h), "--screenshot=$file") + if ($s.dark) { $edgeArgs += '--force-dark-mode' } + $edgeArgs += ($base + '?page=' + $s.page) + $p = Start-Process -FilePath $edge -ArgumentList $edgeArgs -Wait -PassThru -NoNewWindow + if (Test-Path $file) { Say ("shot {0} {1} bytes" -f $s.name, (Get-Item $file).Length) } else { Say ("shot {0} FAILED, edge exit {1}" -f $s.name, $p.ExitCode) } +} + +$relay = 'https://relay.igneum.network/api/relay?fn=drop' +$sent = 0 +foreach ($png in Get-ChildItem $out -Filter '*.png') { + try { + $bytes = [IO.File]::ReadAllBytes($png.FullName) + if ($bytes.Length -gt 4000000) { Say ("not sent {0}: {1} bytes is over the raw cap" -f $png.Name, $bytes.Length); continue } + $hdr = @{ 'x-igneum-key' = $env:IGNEUM_INTAKE_KEY; 'x-file-name' = $png.Name; 'x-title' = ('site-capture ' + $png.Name); 'x-from' = 'pc1' } + $r = Invoke-RestMethod -Uri $relay -Method Post -Headers $hdr -Body $bytes -ContentType 'image/png' -TimeoutSec 120 + Say ("sent {0} as relay item {1}" -f $png.Name, $r.id) + $sent++ + } catch { Say ("not sent {0}: {1}" -f $png.Name, $_.Exception.Message) } +} +Say ("done: {0} of {1} shots on the relay" -f $sent, $shots.Count) +if ($sent -lt $shots.Count) { exit 1 } diff --git a/site/api/live.mjs b/site/api/live.mjs index 10b1221af..57012a989 100644 --- a/site/api/live.mjs +++ b/site/api/live.mjs @@ -1,5 +1,5 @@ // Igneum live devnet feed. GET /api/live returns what tools/observer wrote to Neon: -// {now, state, blocks (last 90 s, or ?window=N up to 300), miners (last 10 min), events (last 30), finality (checkpoints, newest lock), +// {now, state (incl. height = chain block number), blocks (last 90 s, or ?window=N up to 300), miners (last 10 min), events (last 30), finality (checkpoints, newest lock), // proving (activation and 10-minute counts; every chain block carries shards: [{i, state, prover, lag, payout}])}. // Five queries, all on indexed columns. Cached one second at the edge. // LIVE_TABLE_PREFIX (default empty) reads a test observer's tables (fintest_live_*, provtest_live_*) instead of the devnet's. @@ -43,7 +43,8 @@ export default async function handler(req, res) { const [head, blockRows, minerRows, checkpointRows, proofRows] = await Promise.all([ sql(`SELECT now() AS now, (SELECT row_to_json(s) FROM ${T}live_state s WHERE s.id = 1) AS state, - (SELECT json_agg(e) FROM (SELECT ts, kind, text FROM ${T}live_events ORDER BY ts DESC LIMIT 30) e) AS events`), + (SELECT json_agg(e) FROM (SELECT ts, kind, text FROM ${T}live_events ORDER BY ts DESC LIMIT 30) e) AS events, + (SELECT max(number) FROM ${T}live_blocks WHERE number IS NOT NULL AND received_at > now() - interval '10 minutes') AS height`), sql(`SELECT hash, blue_score, daa_score, timestamp_ms, parent_hashes, is_chain_block, vote_key_hash, received_at, color FROM ${T}live_blocks WHERE received_at > now() - ($1 || ' seconds')::interval @@ -78,6 +79,9 @@ export default async function handler(req, res) { age_s: age_s === null ? null : Math.round(age_s * 10) / 10, network: s ? s.network : null, node_version: s ? s.node_version : null, + // 6 Oct 2026: height is the chain block number (the EVM block number of the newest chain block the observer planned), + // the same field /api/stats reports; block_count counts every DAG block the node holds (pruned nodes hold fewer) + height: num(head[0].height), block_count: s ? num(s.block_count) : null, header_count: s ? num(s.header_count) : null, blue_score: s ? num(s.blue_score) : null, diff --git a/site/build.mjs b/site/build.mjs index 1e16aa94a..ac4fae632 100644 --- a/site/build.mjs +++ b/site/build.mjs @@ -255,7 +255,7 @@ const PRODUCT = { full: 'Igneum Ember', name: 'Ember', app: 'Igneum Miner', - caption: 'Igneum Ember (the app is still labelled Igneum Miner until 0.3.6)', + caption: 'Igneum Ember (the app window still says Igneum Miner)', }; function stampProduct(html, file) { return html.replace(/[^<]*<\/span>/g, (m, k) => { diff --git a/site/downloads.json b/site/downloads.json index d36b3c69c..f78ff42d9 100644 --- a/site/downloads.json +++ b/site/downloads.json @@ -3,27 +3,27 @@ "files": { "miner-hive": { "alias": "/public/igneum-miner-hive.tar.gz", - "file": "igneum-hive-0.3.9.tar.gz", - "path": "/dl/public/igneum-hive-0.3.9.tar.gz", - "sha256": "7a58a30fd47c9ecb3d4aeaa0c0a464550f7e4b2b33eacf87512f1b72164c829e", - "size": 24179978, - "version": "0.3.9" + "file": "igneum-hive-0.3.14.tar.gz", + "path": "/dl/public/igneum-hive-0.3.14.tar.gz", + "sha256": "c4699f2487580dcf06b5c22a36a2eb8383a3307b6137699c76c45eefebdd4c88", + "size": 24659507, + "version": "0.3.14" }, "miner-mac": { "alias": "/public/igneum-miner-mac.dmg", - "file": "Igneum-Miner-0.3.10.dmg", - "path": "/dl/public/Igneum-Miner-0.3.10.dmg", - "sha256": "151687c5672553c571af7224a8e4228348135b8a313fc89e6641db7ae21c85b3", - "size": 41383375, - "version": "0.3.10" + "file": "Igneum-Miner-0.3.14.dmg", + "path": "/dl/public/Igneum-Miner-0.3.14.dmg", + "sha256": "e9e4c24db81a262a36b6bac11609ce6d24ed03220973da4142df935d41fc4e40", + "size": 41892337, + "version": "0.3.14" }, "miner-windows": { "alias": "/public/igneum-miner-windows.exe", - "file": "Igneum-Miner-Setup-0.3.10.exe", - "path": "/dl/public/Igneum-Miner-Setup-0.3.10.exe", - "sha256": "24e58849f2b517e8c5579455e8c9d8376ff7dd27052f458ef91913cbc48abc88", - "size": 49858273, - "version": "0.3.10" + "file": "Igneum-Miner-Setup-0.3.14.exe", + "path": "/dl/public/Igneum-Miner-Setup-0.3.14.exe", + "sha256": "08e75effc732f88bca11006b3b7cdf2bc533576f56819f79509fee47a8f5dd59", + "size": 45416619, + "version": "0.3.14" }, "wallet-mac": { "alias": "/public/igneum-wallet-mac.dmg", @@ -34,5 +34,5 @@ "version": "0.1.4" } }, - "updated": "2026-10-05T21:52:34Z" + "updated": "2026-10-06T17:48:20Z" } diff --git a/site/img/miner-dashboard-light.webp b/site/img/miner-dashboard-light.webp new file mode 100644 index 000000000..42aebc0a9 Binary files /dev/null and b/site/img/miner-dashboard-light.webp differ diff --git a/site/img/miner-dashboard.webp b/site/img/miner-dashboard.webp index ade0dafcf..818904d85 100644 Binary files a/site/img/miner-dashboard.webp and b/site/img/miner-dashboard.webp differ diff --git a/site/img/miner-earnings.webp b/site/img/miner-earnings.webp new file mode 100644 index 000000000..fb49ee2e2 Binary files /dev/null and b/site/img/miner-earnings.webp differ diff --git a/site/img/miner-phone.webp b/site/img/miner-phone.webp new file mode 100644 index 000000000..1034621bc Binary files /dev/null and b/site/img/miner-phone.webp differ diff --git a/site/img/miner-prove.webp b/site/img/miner-prove.webp new file mode 100644 index 000000000..e83ab8bfc Binary files /dev/null and b/site/img/miner-prove.webp differ diff --git a/site/img/miner-settings.webp b/site/img/miner-settings.webp new file mode 100644 index 000000000..4b4b23abd Binary files /dev/null and b/site/img/miner-settings.webp differ diff --git a/site/index.html b/site/index.html index 7f3d2473d..344848da9 100644 --- a/site/index.html +++ b/site/index.html @@ -204,6 +204,8 @@ section:not(.light)+section:not(.light){padding-top:0;scroll-margin-top:var(--sm .blk.new{animation:pop .4s ease} @keyframes pop{from{transform:scale(.4);opacity:0}to{transform:scale(1);opacity:1}} .legend{display:flex;flex-wrap:wrap;gap:20px;margin-top:24px;font-size:14px;color:var(--ink-2)} +.viz-note{margin:14px 0 0;font-size:13px;line-height:1.5;color:var(--ash);max-width:80ch} +.legend[hidden]{display:none} .legend span{display:inline-flex;align-items:center;gap:8px} .sw{width:14px;height:14px;border-radius:4px;display:inline-block} pre{margin:0;font-family:var(--f-mono);font-size:13px;line-height:1.6;color:var(--ink-2);white-space:pre-wrap;min-height:9.6em} @@ -372,7 +374,7 @@ pre{margin:0;font-family:var(--f-mono);font-size:13px;line-height:1.6;color:var(
0
vote keys active in 10 min (a card runs several)
0.0
blocks / s
0.0 MH/s
hash rate
-
0
devnet blocks
+
0
chain block
@@ -394,18 +396,26 @@ pre{margin:0;font-family:var(--f-mono);font-size:13px;line-height:1.6;color:var(

Watch the chain prove itself

Blocks every second, proven in shards, locked every 30 seconds. First live lock 4 Oct 2026: checkpoint 242, 77.4% of all weight, two hours after genesis.

-
+
simulated preview
-
blocks 0proven 0locked 0
+
blocks 0proven 0locked 0
-
+
mined shards being proven proven locked checkpoint, final
+ +
@@ -488,8 +498,8 @@ pre{margin:0;font-family:var(--f-mono);font-size:13px;line-height:1.6;color:var(
Igneum Miner
- The miner dashboard: hash rate, blocks found, node synced, a countdown to the next program, the block chart, the Apple M5 Max card mining, node and finality panels -
Igneum Ember (the app is still labelled Igneum Miner until 0.3.6) on an Apple M5 Max, live devnet, 5 Oct 2026.
+ The Mine page of Igneum Miner 0.3.14: the Start mining button, the live total, the card row for an Apple M5 Max with its state word, Tune button and switch, the Tuning card with Efficiency, Balanced and Maximum, the node as one line, and the activity strip +
Igneum Ember (the app window still says Igneum Miner) 0.3.14 on an Apple M5 Max, paused, live devnet, 6 Oct 2026.
@@ -497,11 +507,12 @@ pre{margin:0;font-family:var(--f-mono);font-size:13px;line-height:1.6;color:var(
NVIDIA, AMD, Apple siliconFound by itself. One worker per card, several identities each.
A new program every hour, no pauseCompiled ahead and swapped in 0.01 ms on the live devnet, 0 rejected blocks.
Signed updates at a safe momentThe card keeps mining through the download. The old version comes back if the new one fails to start.
-
Tunes itself, in publicKernel variants raced every hour, hash per watt swept, every number in the bench table.
+
Four pages: Mine, Earnings, Prove, SettingsEvery card is one row: rate, watts, MH per watt, pounds a day at your electricity price, a Tune button. The node is one line.
+
Ember Tune, measuredAn RTX 5090 from 311 W to 227 W for 0.15% of its rate; an RTX 4070 from 106 W to 76 W for none (PC 1, 6 Oct 2026). Every number in the bench table.

Devnet: coins have no value and the chain may be reset.

@@ -665,7 +676,7 @@ pre{margin:0;font-family:var(--f-mono);font-size:13px;line-height:1.6;color:var( - +