The owner's orders of 7 October 2026 (header, logos, footer, phone scene) and the Counter ASIC lane's public text (docs/plans/counter-asic-3-public-text-2026-10-07.md, b10c7ac2) with the disclosure prize held back until its publish word.
Header: the one-screen home no longer hides items; the sheet carries exactly Litepaper, Miner, App, Wallet, Live devnet, Ledger and Download; 44 px bar links, 48 px sheet rows, the burger turns into an X; tools/ci/site-nav-check.mjs runs after every build and in the gate.
Cards: the Windows four-pane, Apple and Tux marks from 80c5618, white on the ember card, the card's own text colour on the mono cards (so they flip with the theme).
Footer: the ledger page renders through tools/ledger-page.mjs and now underlines Ledger; the Reddit profile is the one the owner named.
Renderer: narrow, narrowBreak 720, narrowWindow 30, narrowLanes 4, narrowMinNode 11; hairlines off except the selected chain; checkpoint labels as the percent; fps 60 on the home and /live mounts. Measured in headless chromium at 390 with 4x CPU throttling: 50 to 56 frames a second (the cap is 60).
Evidence row 17 rewritten in the table's eight columns. The forbidden list gains "disclosure prize" and the word-bounded rig names.
Tested with Playwright at 390 and 768 in both themes with real taps on every public page and the 404: 48 rows, 0 problems; captures in docs/plans/site-ui-4-shots/chrome.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
538 lines
51 KiB
HTML
538 lines
51 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||
<title>Igneum live devnet</title>
|
||
<meta name="description" content="The Igneum network observatory: every block and its connections, the miners, the provers paid and the finality rule, read from a node every two seconds.">
|
||
<link rel="canonical" href="https://igneum.network/live">
|
||
<meta name="theme-color" content="#0C0C0E">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:site_name" content="Igneum">
|
||
<meta property="og:title" content="Igneum live devnet">
|
||
<meta property="og:description" content="The Igneum devnet as it happens: every block, the miners, the provers paid and the finality rule, read from a node every two seconds.">
|
||
<meta property="og:url" content="https://igneum.network/live">
|
||
<meta property="og:image" content="https://igneum.network/og-small.png?v=3">
|
||
<meta property="og:image:width" content="256">
|
||
<meta property="og:image:height" content="256">
|
||
<meta property="og:image:alt" content="Igneum. Mined by GPUs. Proven by fire.">
|
||
<meta name="twitter:card" content="summary">
|
||
<meta name="twitter:title" content="Igneum live devnet">
|
||
<meta name="twitter:description" content="The Igneum devnet as it happens: every block, the miners, the provers paid and the finality rule, read from a node every two seconds.">
|
||
<meta name="twitter:image" content="https://igneum.network/og-small.png?v=3">
|
||
<meta name="twitter:image:alt" content="Igneum. Mined by GPUs. Proven by fire.">
|
||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Crect width='1024' height='1024' fill='%230C0C0E'/%3E%3Cg transform='translate(166.95 166.95) scale(6.901)'%3E%3Cpolygon points='50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34' fill='%23F2541B'/%3E%3Cpolygon points='50,42 59,58 50,82 41,58' fill='%230C0C0E'/%3E%3C/g%3E%3C/svg%3E" type="image/svg+xml">
|
||
<link rel="icon" href="/favicon.ico" sizes="48x48">
|
||
<link rel="icon" href="/favicon-32.png" type="image/png" sizes="32x32">
|
||
<link rel="icon" href="/icon-192.png" type="image/png" sizes="192x192">
|
||
<link rel="icon" href="/icon-512.png" type="image/png" sizes="512x512">
|
||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
||
<link rel="manifest" href="/site.webmanifest">
|
||
<!-- head:start -->
|
||
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
|
||
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
|
||
<link rel="stylesheet" href="/site.css">
|
||
<style>
|
||
/* Shared by every page: the head partial, injected by the build between the head markers. Edit the partial, not the page. */
|
||
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. The tokens, type and components are in /site.css (docs/plans/site-ui-3.md). */
|
||
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/plex-sans-400.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/plex-sans-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:600;font-display:swap;src:url(/fonts/plex-sans-600.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/plex-mono-400.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/plex-mono-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
||
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
|
||
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
|
||
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
|
||
</style>
|
||
<script>
|
||
/* theme before first paint: the footer control stores "light" or "dark" under igneum-theme; nothing stored means the system setting */
|
||
(function(){try{var t=localStorage.getItem("igneum-theme");if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t);}catch(e){}})();
|
||
</script>
|
||
<!-- head:end -->
|
||
<style>
|
||
/* live page block (7 Oct 2026, site-ui-4: the observatory composition, every number read from the observer); tokens and components are in /site.css */
|
||
.obs{--pr:8px;--hair:1px solid var(--line)}
|
||
.obs-head{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:var(--s-4) var(--s-5);padding:var(--s-6) 0 var(--s-5)}
|
||
.obs-head .eyebrow{display:flex;align-items:center;gap:8px;margin-bottom:var(--s-3)}
|
||
.obs-head .eyebrow i{width:7px;height:7px;background:var(--ember);transform:rotate(45deg);display:inline-block}
|
||
.obs-head h1{font-size:clamp(34px,4.6vw,56px);font-weight:700;letter-spacing:-.02em;margin:0}
|
||
.obs-head h1 b{color:var(--ember-text)}
|
||
.obs-head p{margin:var(--s-3) 0 0;color:var(--ink-2);max-width:60ch}
|
||
.obs-head .side{display:flex;flex-direction:column;align-items:flex-end;gap:var(--s-2)}
|
||
.obs-head .side .state{font-size:var(--t-xs)}
|
||
.tiles6{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));border:var(--hair);border-radius:var(--pr);background:var(--graphite);overflow:hidden}
|
||
.tiles6>div{padding:18px 19px 19px;border-right:var(--hair);min-width:0}
|
||
.tiles6>div:last-child{border-right:0}
|
||
.tiles6 .k{display:flex;justify-content:space-between;align-items:center;font:400 13px/1.4 var(--f-sans);color:var(--ink-2)}
|
||
.tiles6 .k i{color:var(--ash);font:400 12px/1 var(--f-mono)}
|
||
.tiles6 .v{font:500 clamp(22px,2vw,30px)/1.15 var(--f-sans);color:var(--bone);margin:14px 0 10px;letter-spacing:-.01em;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
|
||
.tiles6 .v.word{font:500 15px/1.3 var(--f-mono);color:var(--ash)}
|
||
.tiles6 .d{font:400 11px/1.4 var(--f-mono);color:var(--ash)}
|
||
@media (max-width:1100px){.tiles6{grid-template-columns:repeat(3,minmax(0,1fr))}.tiles6>div:nth-child(3){border-right:0}.tiles6>div:nth-child(-n+3){border-bottom:var(--hair)}}
|
||
@media (max-width:600px){.tiles6{grid-template-columns:repeat(2,minmax(0,1fr))}.tiles6>div{border-right:var(--hair)!important;border-bottom:var(--hair)}.tiles6>div:nth-child(2n){border-right:0!important}.tiles6>div:nth-last-child(-n+2){border-bottom:0}}
|
||
.panel{border:var(--hair);border-radius:var(--pr);background:var(--graphite);margin-top:var(--s-5);overflow:hidden}
|
||
.toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:12px;padding:15px 21px;border-bottom:var(--hair);background:var(--row)}
|
||
.toolbar .title{display:flex;align-items:center;gap:10px;font:500 var(--t-md)/1.3 var(--f-sans);color:var(--bone)}
|
||
.toolbar .title i{width:14px;height:14px;border:1.5px solid var(--ember);border-radius:50%;display:inline-block;position:relative}
|
||
.toolbar .title i::after{content:"";position:absolute;inset:4px;background:var(--ember);border-radius:50%}
|
||
.chip{font:400 11px/1.2 var(--f-mono);color:var(--ash);border:var(--hair);border-radius:4px;padding:5px 8px;white-space:nowrap}
|
||
.toolbar .right{margin-left:auto;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
|
||
.tb{background:transparent;border:1px solid transparent;color:var(--ash);font:400 13px/1.2 var(--f-sans);padding:7px 10px;border-radius:6px;cursor:pointer}
|
||
.tb:hover{color:var(--bone)}
|
||
.tb[aria-pressed="true"]{color:var(--bone);border-color:var(--line-2);background:var(--graphite)}
|
||
.tb.on{color:var(--ember-text);border-color:var(--ember-40)}
|
||
.sep{width:1px;height:18px;background:var(--line)}
|
||
.graph{display:grid;grid-template-columns:minmax(0,1fr) 250px}
|
||
@media (max-width:1000px){.graph{grid-template-columns:1fr}}
|
||
.gcol{padding:18px 21px 14px;min-width:0;border-right:var(--hair)}
|
||
@media (max-width:1000px){.gcol{border-right:0;border-bottom:var(--hair)}}
|
||
.gcol .state{margin-bottom:10px;font-size:var(--t-xs)}
|
||
.scene{position:relative}
|
||
#dag{display:block;width:100%;height:clamp(300px,36vw,440px);border-radius:6px;background:var(--row);touch-action:pan-y}
|
||
.tip{position:absolute;left:0;top:0;pointer-events:none;background:var(--graphite);border:1px solid var(--line-2);border-radius:6px;padding:10px 12px;font:400 12px/1.5 var(--f-mono);color:var(--ink-2);white-space:nowrap;z-index:5}
|
||
.tip[hidden]{display:none}
|
||
.gfoot{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;margin-top:12px;font:400 11px/1.4 var(--f-mono);color:var(--ash)}
|
||
.zoom{display:inline-flex;align-items:center;border:var(--hair);border-radius:6px;overflow:hidden}
|
||
.zoom button{background:transparent;border:0;color:var(--ink-2);font:400 13px/1 var(--f-mono);padding:8px 12px;cursor:pointer}
|
||
.zoom button:hover{background:var(--hover)}
|
||
.zoom span{font:400 11px/1 var(--f-mono);color:var(--ash);padding:0 6px;min-width:44px;text-align:center}
|
||
.icol{padding:18px 21px;min-width:0;font-size:13px}
|
||
.icol .ih{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-bottom:14px}
|
||
.icol .ih .eyebrow{margin:0}
|
||
.icol .num{font:500 22px/1.2 var(--f-sans);color:var(--bone);margin:6px 0 4px;overflow-wrap:anywhere}
|
||
.icol .hash{font:400 11px/1.5 var(--f-mono);color:var(--ash);overflow-wrap:anywhere;margin-bottom:14px}
|
||
.insp{display:grid;grid-template-columns:auto minmax(0,1fr);gap:0 10px;margin:0;border-top:var(--hair);padding-top:8px}
|
||
.insp dt{font:400 11px/1.3 var(--f-mono);color:var(--ash);padding:6px 0}
|
||
.insp dd{margin:0;font:400 11px/1.3 var(--f-mono);color:var(--bone);text-align:right;padding:6px 0;overflow-wrap:anywhere;min-width:0}
|
||
.insp dd.hot{color:var(--molten-text)}
|
||
.icol .note{font-size:12px;margin-top:14px;padding-top:12px;border-top:var(--hair)}
|
||
.insp-empty{color:var(--ash);font-size:13px;line-height:1.5;padding:6px 0 2px}
|
||
.mstats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));border-bottom:var(--hair)}
|
||
.mstats>div{padding:16px 21px 14px;border-right:var(--hair);min-width:0}
|
||
.mstats>div:last-child{border-right:0}
|
||
.mstats .mk{font:400 13px/1.4 var(--f-sans);color:var(--ink-2)}
|
||
.mstats .mv{display:flex;align-items:baseline;gap:8px;margin:8px 0 6px}
|
||
.mstats .mv b{font:500 26px/1.1 var(--f-sans);color:var(--bone);letter-spacing:-.01em}
|
||
.mstats .mv b.own{color:var(--molten-text)}.mstats .mv b.dim{color:var(--ash)}
|
||
.mstats .mv small{font:400 11px/1.4 var(--f-mono);color:var(--ash)}
|
||
.mstats .mf{font:400 11px/1.4 var(--f-mono);color:var(--ash);overflow-wrap:anywhere}
|
||
@media (max-width:900px){.mstats{grid-template-columns:repeat(2,minmax(0,1fr))}.mstats>div:nth-child(2){border-right:0}.mstats>div:nth-child(-n+2){border-bottom:var(--hair)}}
|
||
.tabs{display:inline-flex;border:var(--hair);border-radius:6px;padding:3px;gap:2px;background:var(--graphite)}
|
||
.tab{background:transparent;border:0;color:var(--ash);font:400 12px/1.2 var(--f-sans);padding:6px 10px;border-radius:4px;cursor:pointer}
|
||
.tab.on{background:var(--row);color:var(--bone)}
|
||
.ititle{font:500 22px/1.2 var(--f-sans);color:var(--bone);margin:6px 0 4px;letter-spacing:-.01em}
|
||
.proof-scene{height:200px;border:var(--hair);border-radius:6px;background:var(--row);margin:12px 0;overflow:hidden}
|
||
.proof-scene canvas{display:block;width:100%;height:100%}
|
||
.proof-head{display:flex;justify-content:space-between;align-items:baseline;font:500 13px/1.3 var(--f-sans);color:var(--bone);margin-bottom:8px}
|
||
.track{display:flex;gap:3px;height:3px;margin-bottom:10px}
|
||
.track i{flex:1 1 0;background:var(--line);border-radius:2px}
|
||
.track i.proving{background:var(--ember)}.track i.verified{background:var(--bone)}.track i.paid{background:var(--molten)}
|
||
.shards{list-style:none;margin:0 0 12px;padding:0;font:400 11px/1.6 var(--f-mono)}
|
||
.shards li{display:flex;justify-content:space-between;gap:8px;color:var(--ink-2)}
|
||
.shards li::before{content:"\25CF";font-size:7px;margin-right:6px;color:var(--line-2);vertical-align:middle}
|
||
.shards li.proving,.shards li.proving::before{color:var(--ember-text)}.shards li.verified,.shards li.verified::before{color:var(--bone)}.shards li.paid,.shards li.paid::before{color:var(--molten-text)}
|
||
.lgi{width:10px;height:10px;border-radius:3px;display:inline-block;flex:none;position:relative}
|
||
.lgi.tick::after{content:"\2713";position:absolute;left:0;top:-4px;font-size:11px;color:var(--bone)}
|
||
.motion{margin-top:var(--s-5);border:var(--hair);border-radius:7px;background:var(--graphite);padding:22px 21px}
|
||
.motion h2{font:500 var(--t-h3)/1.3 var(--f-sans);margin:0 0 14px;color:var(--bone)}
|
||
.beats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-4)}
|
||
@media (max-width:900px){.beats{grid-template-columns:1fr}}
|
||
.beats .num{font:400 10px/1 var(--f-mono);color:var(--ember-text)}
|
||
.beats h3{font:500 14px/1.4 var(--f-sans);margin:6px 0 4px;color:var(--bone)}
|
||
.beats p{margin:0;font-size:13px;color:var(--ink-2);line-height:1.5}
|
||
.legend-row{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px 18px;padding:12px 21px;border-top:var(--hair);font:400 11px/1.4 var(--f-mono);color:var(--ash)}
|
||
.legend-row .lg{display:flex;flex-wrap:wrap;gap:8px 16px}
|
||
.legend-row .lg span{display:inline-flex;align-items:center;gap:7px;color:var(--ink-2);font-family:var(--f-sans);font-size:12px}
|
||
.sw{width:9px;height:9px;transform:rotate(45deg);display:inline-block;flex:none}
|
||
.three{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-4);margin-top:var(--s-4)}
|
||
@media (max-width:1000px){.three{grid-template-columns:1fr}}
|
||
.dp{border:var(--hair);border-radius:7px;background:var(--graphite);padding:20px 21px 16px;min-width:0}
|
||
.dp .ph{display:flex;justify-content:space-between;align-items:baseline;gap:10px;margin-bottom:14px}
|
||
.dp h2{font:500 var(--t-md)/1.3 var(--f-sans);margin:0;color:var(--bone)}
|
||
.dp .tag{font:400 10px/1.2 var(--f-mono);letter-spacing:.12em;text-transform:uppercase;color:var(--ash)}
|
||
.big{display:flex;align-items:baseline;gap:10px;margin-bottom:8px}
|
||
.big b{font:500 30px/1 var(--f-sans);color:var(--bone)}
|
||
.big span{font:400 12px/1.4 var(--f-sans);color:var(--ash)}
|
||
.bars{display:flex;align-items:flex-end;gap:3px;height:80px;margin-top:10px}
|
||
.bars i{flex:1 1 0;background:linear-gradient(to top,var(--ember-40),var(--molten));border-radius:2px 2px 0 0;min-height:2px;opacity:.9}
|
||
.bars i.now{background:var(--molten)}
|
||
.axis{display:flex;justify-content:space-between;font:400 10px/1.4 var(--f-mono);color:var(--ash);margin-top:8px}
|
||
.ids{display:flex;flex-direction:column;gap:12px}
|
||
.id-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:4px 10px;font:400 12px/1.4 var(--f-mono);color:var(--ink-2);align-items:center}
|
||
.id-row .name{display:flex;align-items:center;gap:8px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||
.id-row .name .sw{background:var(--line-2)}
|
||
.id-row.mine .name{color:var(--ember-text)}
|
||
.id-row.mine .name .sw{background:var(--ember)}
|
||
.id-row .pct{color:var(--bone);white-space:nowrap}
|
||
.id-row .bar{grid-column:1/-1;height:3px;background:var(--line);border-radius:2px;overflow:hidden}
|
||
.id-row .bar i{display:block;height:100%;background:var(--ember);opacity:.85}
|
||
.id-row.mine .bar i{background:var(--molten)}
|
||
.evs{display:flex;flex-direction:column;max-height:330px;overflow:auto}
|
||
.evs div{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:10px;align-items:baseline;padding:9px 0;border-bottom:var(--hair);font:400 12px/1.4 var(--f-mono);color:var(--ink-2)}
|
||
.evs div:last-child{border-bottom:0}
|
||
.evs .dot{width:6px;height:6px;background:var(--molten);border-radius:50%;animation:none}
|
||
.evs .dot.lock{background:var(--ember)}.evs .dot.quiet{background:var(--ash)}
|
||
.evs .what{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||
.evs .what b{font-weight:500;color:var(--bone)}
|
||
.evs .t{color:var(--ash);font-size:11px;white-space:nowrap}
|
||
.evs div.new{animation:land .9s ease-out}
|
||
@keyframes land{0%{background:var(--ember-12)}100%{background:transparent}}
|
||
@media (prefers-reduced-motion:reduce){.evs div.new{animation:none}}
|
||
.tablebar{margin-top:var(--s-4);border:var(--hair);border-radius:7px;background:var(--graphite)}
|
||
.tablebar summary{padding:16px 21px;font:500 var(--t-md)/1.3 var(--f-sans);color:var(--bone)}
|
||
.tablebar summary::after{content:"+";font:400 14px/1 var(--f-mono);color:var(--ash)}
|
||
.tablebar[open] summary::after{content:"\2212"}
|
||
.tablebar summary small{font:400 11px/1.4 var(--f-mono);color:var(--ash);margin-left:auto;margin-right:12px}
|
||
.tablebar .tbl{border:0;border-top:var(--hair);border-radius:0}
|
||
.tablebar table{font-size:12px;font-family:var(--f-mono)}
|
||
.tablebar td,.tablebar th{padding:8px 21px}
|
||
.tablebar tr[data-hash]{cursor:pointer}
|
||
.tablebar tr[data-hash]:hover td{background:var(--hover)}
|
||
.obs-foot{display:flex;flex-wrap:wrap;justify-content:space-between;gap:10px;margin-top:var(--s-5);font:400 10px/1.5 var(--f-mono);letter-spacing:.1em;text-transform:uppercase;color:var(--ash)}
|
||
.linkish{background:none;border:0;padding:0;color:var(--ember-text);font:inherit;cursor:pointer}
|
||
.linkish:hover{text-decoration:underline}
|
||
details.tablebar{border-top:var(--hair);border-bottom:var(--hair)}
|
||
details.tablebar summary{display:flex;align-items:center}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- nav:start -->
|
||
<a class="skip" href="#main">Skip to content</a>
|
||
<nav class="nav" aria-label="Main">
|
||
<div class="wrap">
|
||
<a href="/" class="brand" aria-label="Igneum home">
|
||
<svg class="mark" viewBox="0 0 1024 1024" width="28" height="28" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
|
||
<span class="word">IGNEUM</span>
|
||
</a>
|
||
<div class="links" id="nav-links">
|
||
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
|
||
<a href="/miner" data-nav="miner">Miner</a>
|
||
<a href="/app" data-nav="app">App</a>
|
||
<a href="/wallet" data-nav="wallet">Wallet</a>
|
||
<a href="/live" data-nav="live" aria-current="page">Live devnet</a>
|
||
<a href="/ledger" data-nav="ledger">Ledger</a>
|
||
</div>
|
||
<a href="/miner#get" class="btn primary small cta">Download</a>
|
||
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Open the menu" aria-haspopup="true"><span class="bars" aria-hidden="true"></span></button>
|
||
<div class="nav-sheet" id="nav-sheet">
|
||
<a href="/litepaper" data-nav="litepaper">Litepaper</a>
|
||
<a href="/miner" data-nav="miner">Miner</a>
|
||
<a href="/app" data-nav="app">App</a>
|
||
<a href="/wallet" data-nav="wallet">Wallet</a>
|
||
<a href="/live" data-nav="live">Live devnet</a>
|
||
<a href="/ledger" data-nav="ledger">Ledger</a>
|
||
<a href="/miner#get" class="btn primary">Download</a>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
<script>
|
||
(function(){
|
||
// the menu: open on the button, closed by any link, Escape (focus back on the button) or a resize past 900 px
|
||
var n=document.querySelector('.nav'),b=document.getElementById('nav-burger'),s=document.getElementById('nav-sheet');if(!n||!b||!s)return;
|
||
function set(o){n.classList.toggle('open',o);b.setAttribute('aria-expanded',o?'true':'false');b.setAttribute('aria-label',o?'Close the menu':'Open the menu');document.documentElement.style.overflow=o?'hidden':'';}
|
||
b.addEventListener('click',function(){set(!n.classList.contains('open'));});
|
||
s.addEventListener('click',function(e){if(e.target.closest('a'))set(false);});
|
||
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&n.classList.contains('open')){set(false);b.focus();}});
|
||
var mq=window.matchMedia('(min-width:901px)');if(mq.addEventListener)mq.addEventListener('change',function(){set(false);});
|
||
})();
|
||
</script>
|
||
<!-- nav:end -->
|
||
|
||
<main id="main" class="wrap obs">
|
||
<header class="obs-head" id="top">
|
||
<div>
|
||
<div class="eyebrow"><i></i>Network observatory</div>
|
||
<h1>Live devnet<b>.</b></h1>
|
||
<p>Every block has a story. Follow the connections.</p>
|
||
</div>
|
||
<div class="side">
|
||
<div class="cta"><a href="/miner#get" class="btn primary">Download the miner</a><a href="/explorer" class="btn">Open the explorer</a></div>
|
||
<div class="state" id="live-state"><span class="dot off"></span>connecting to the observer</div>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="tiles6" aria-live="polite">
|
||
<div><div class="k">Blocks / second <i>↗</i></div><div class="v tick" id="s-bps">pending</div><div class="d">counted over the node’s last 60 s; the target is 1</div></div>
|
||
<div><div class="k">Blue score <i>#</i></div><div class="v tick" id="s-blue">pending</div><div class="d">the newest block’s count of blue ancestors</div></div>
|
||
<div><div class="k">Network hash rate <i>∿</i></div><div class="v tick" id="s-hash">pending</div><div class="d">the node’s estimate from the last 1,000 blocks’ difficulty</div></div>
|
||
<div><div class="k">Active vote keys <i>◇</i></div><div class="v tick" id="s-keys">pending</div><div class="d">keys with a block in 10 min; a card runs several</div></div>
|
||
<div><div class="k">Connected peers <i>⇄</i></div><div class="v tick" id="s-peers">pending</div><div class="d">peers of the observer’s node</div></div>
|
||
<div><div class="k">Locked checkpoint <i>●</i></div><div class="v tick" id="s-lock">pending</div><div class="d" id="s-lock-d">signed by two thirds of all 30-day weight</div></div>
|
||
</div>
|
||
|
||
<section class="panel" aria-label="The block graph">
|
||
<div class="mstats">
|
||
<div><div class="mk">Blocks in window</div><div class="mv"><b id="m-blocks">0</b><small id="m-win">last 90 s</small></div><div class="mf" id="m-keys">waiting for the observer</div></div>
|
||
<div><div class="mk">Your blocks</div><div class="mv"><b id="m-mine" class="own">0</b><small>in this window</small></div><div class="mf">Key <span class="mono" id="m-key">not set</span> · <button type="button" class="linkish" id="mine-btn">Mark my key</button></div></div>
|
||
<div><div class="mk">Proven blocks</div><div class="mv"><b id="m-proven">0</b><small id="m-proven-pct">no blocks</small></div><div class="mf">observer-reported proof status</div></div>
|
||
<div><div class="mk">Latest checkpoint</div><div class="mv"><b id="m-cp">None</b><small id="m-cp-w"></small></div><div class="mf" id="m-cp-note">no checkpoint in the reply</div></div>
|
||
</div>
|
||
<div class="toolbar">
|
||
<div class="title"><i></i>The block graph</div>
|
||
<span class="chip" id="c-count">0 blocks</span>
|
||
<div class="tabs" role="tablist" aria-label="Which blocks">
|
||
<button type="button" class="tab on" data-filter="all" aria-pressed="true">All blocks</button>
|
||
<button type="button" class="tab" data-filter="chain" aria-pressed="false">Selected chain</button>
|
||
<button type="button" class="tab" data-filter="mine" aria-pressed="false">Your blocks</button>
|
||
</div>
|
||
<div class="right">
|
||
<span class="chip" id="lane-caption">keys</span>
|
||
<span class="sep"></span>
|
||
<button type="button" class="tb" id="dag-pause" aria-pressed="false">Pause</button>
|
||
<button type="button" class="tb on" id="dag-follow">Follow</button>
|
||
</div>
|
||
</div>
|
||
<div class="graph">
|
||
<div class="gcol">
|
||
<div class="state" id="dag-state"><span class="dot off"></span>connecting to the observer</div>
|
||
<div class="scene"><canvas id="dag" aria-label="The devnet's block graph, live: time-aligned miner lanes, every parent connection, the selected chain as one path, checkpoint bands, your blocks framed"></canvas><div class="tip" id="dag-tip" hidden></div><div class="chip-zoom" id="dag-chip" hidden>scroll to zoom · Esc to release</div></div>
|
||
<div class="gfoot">
|
||
<span>Click to inspect · Drag to explore</span>
|
||
<span class="zoom"><button type="button" id="z-out" aria-label="Wider window">−</button><span id="z-pct">60 s</span><button type="button" id="z-in" aria-label="Narrower window">+</button></span>
|
||
</div>
|
||
</div>
|
||
<aside class="icol" id="inspector" aria-live="polite">
|
||
<div class="ih"><div class="eyebrow">Block inspector</div><span class="chip" id="i-kind">select a block</span></div>
|
||
<h3 class="ititle" id="i-title">Select a block</h3>
|
||
<div class="hash" id="i-hash">Click the graph, or a row of the table below.</div>
|
||
<div class="proof-scene"><canvas id="proof" aria-label="Select a block to see its shards"></canvas></div>
|
||
<div class="proof-head"><span>Proof shards</span><span id="i-shard-count" class="mono">not set</span></div>
|
||
<div class="track" id="i-track"></div>
|
||
<ul class="shards" id="i-shards"><li class="muted">No block selected.</li></ul>
|
||
<dl class="insp">
|
||
<dt>Inclusion</dt><dd id="i-incl">not set</dd>
|
||
<dt>Miner key</dt><dd id="i-miner">not set</dd>
|
||
<dt>Blue score</dt><dd id="i-blue">not set</dd>
|
||
<dt>DAA score</dt><dd id="i-daa">not set</dd>
|
||
<dt>Parent links</dt><dd id="i-parents">not set</dd>
|
||
<dt>Header time</dt><dd id="i-time">not set</dd>
|
||
<dt>Checkpoint</dt><dd id="i-cp">not set</dd>
|
||
<dt>Finality</dt><dd id="i-lock">not set</dd>
|
||
</dl>
|
||
<p class="note">Proof, chain selection and finality are separate states. None is inferred from a block’s age; a lock is drawn only when the observer reports one.</p>
|
||
<p style="margin:10px 0 0"><button type="button" class="linkish" id="insp-close" hidden>Clear</button></p>
|
||
</aside>
|
||
</div>
|
||
<div class="legend-row">
|
||
<div class="lg">
|
||
<span><i class="lgi" style="border:1.5px solid var(--included)"></i>Included</span>
|
||
<span><i class="lgi" style="border:1.5px solid var(--excluded)"></i>Excluded</span>
|
||
<span><i class="lgi" style="border:1.5px solid var(--ember);background:var(--ember-12)"></i>Selected chain</span>
|
||
<span><i class="lgi" style="border:1.5px dashed var(--molten)"></i>Your block</span>
|
||
<span><i class="lgi tick"></i>Proven</span>
|
||
<span><i class="lgi" style="border:1.5px solid var(--ember);border-radius:50%"></i>Locked checkpoint</span>
|
||
</div>
|
||
<span id="blue-range">blue score pending</span>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="motion">
|
||
<h2>Motion, with meaning.</h2>
|
||
<div class="beats">
|
||
<div><span class="num">01</span><h3>A block arrives.</h3><p>A brief signal traces its real parent edges. Your blocks take a molten frame.</p></div>
|
||
<div><span class="num">02</span><h3>The proof takes shape.</h3><p>Shard tiles follow the planned, proving, verified and paid states the observer reports.</p></div>
|
||
<div><span class="num">03</span><h3>A checkpoint locks.</h3><p>A pending marker becomes a solid band only when the observer reports it locked.</p></div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="three">
|
||
<div class="dp">
|
||
<div class="ph"><h2>Block activity</h2><span class="tag">header times / window</span></div>
|
||
<div class="big"><b id="a-count">0</b><span>blocks in this view</span></div>
|
||
<div class="bars" id="a-bars" aria-label="Blocks per minute over the last hour, newest on the right"></div>
|
||
<div class="axis"><span>one bar a minute, the last 30 min</span><span>now</span></div>
|
||
<p class="note" id="a-note" style="margin-top:12px;font-size:12px">Counted by the observer as blocks arrive.</p>
|
||
</div>
|
||
<div class="dp">
|
||
<div class="ph"><h2>Mining identities</h2><span class="tag">displayed window</span></div>
|
||
<div class="ids" id="miners"><div class="empty">No blocks in the window.</div></div>
|
||
<p class="note" style="margin-top:14px;font-size:12px" id="miners-more">Vote-key share of the displayed blocks. Several keys may belong to one GPU.</p>
|
||
</div>
|
||
<div class="dp">
|
||
<div class="ph"><h2>Event stream</h2><span class="tag" id="ev-count">live</span></div>
|
||
<div class="evs" id="events"><div class="empty">Waiting for the first read.</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<details class="tablebar" id="table">
|
||
<summary>Explore blocks as a table <small>keyboard-accessible view</small></summary>
|
||
<div class="tbl"><table>
|
||
<thead><tr><th>Blue score</th><th>Hash</th><th>Vote key</th><th>Parents</th><th>State</th><th>Chain block</th></tr></thead>
|
||
<tbody id="tbody"><tr><td colspan="6">Waiting for the first read.</td></tr></tbody>
|
||
</table></div>
|
||
</details>
|
||
|
||
<div class="obs-foot"><span>Igneum / observatory</span><span>One node read every 2 s. Nothing here is a replay.</span></div>
|
||
</main>
|
||
|
||
<!-- footer:start -->
|
||
<footer class="foot">
|
||
<div class="wrap">
|
||
<div class="foot-grid">
|
||
<div class="foot-brand">
|
||
<a href="/" class="brand" aria-label="Igneum home"><svg class="mark" viewBox="0 0 1024 1024" width="26" height="26" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span></a>
|
||
<p>Mined by GPUs. Proven by fire.</p>
|
||
<div class="social" aria-label="Igneum elsewhere">
|
||
<a href="https://github.com/igneum-network/spec" target="_blank" rel="noopener" aria-label="Igneum on GitHub: the spec, the vectors and the issues"><svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.3.8-.6v-2.1c-3.2.7-3.9-1.4-3.9-1.4-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2.9-.3 1.9-.4 2.9-.4s2 .1 2.9.4c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.2c0 .3.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z"></path></svg></a>
|
||
<a href="https://discord.gg/igneum" target="_blank" rel="noopener" aria-label="The Igneum Discord"><svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor" aria-hidden="true"><path d="M19.6 5.3A17 17 0 0 0 15.4 4l-.2.4a15.6 15.6 0 0 1 3.9 1.9 13.6 13.6 0 0 0-14.2 0A15.6 15.6 0 0 1 8.8 4.4L8.6 4a17 17 0 0 0-4.2 1.3C1.8 9.2 1.1 13 1.4 16.7a17.1 17.1 0 0 0 5.2 2.6l1.1-1.8a10.8 10.8 0 0 1-1.7-.8l.4-.3a12.2 12.2 0 0 0 11.2 0l.4.3-1.7.8 1.1 1.8a17 17 0 0 0 5.2-2.6c.4-4.3-.7-8-3-11.4zM8.7 14.4c-1 0-1.8-.9-1.8-2.1s.8-2.1 1.8-2.1 1.9 1 1.8 2.1c0 1.2-.8 2.1-1.8 2.1zm6.6 0c-1 0-1.8-.9-1.8-2.1s.8-2.1 1.8-2.1 1.9 1 1.8 2.1c0 1.2-.8 2.1-1.8 2.1z"></path></svg></a>
|
||
<a data-social="reddit" href="https://www.reddit.com/user/Igneum_network/" target="_blank" rel="noopener" aria-label="Igneum on Reddit"><svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor" aria-hidden="true"><path d="M22 12.1a2.2 2.2 0 0 0-3.7-1.6 10.8 10.8 0 0 0-5.8-1.8l1-4.6 3.2.7a1.5 1.5 0 1 0 .2-.9l-3.6-.8a.5.5 0 0 0-.5.4l-1.1 5.2a10.8 10.8 0 0 0-5.9 1.8A2.2 2.2 0 1 0 3.4 14a4.3 4.3 0 0 0 0 .7c0 3.4 3.9 6.1 8.6 6.1s8.6-2.7 8.6-6.1a4.3 4.3 0 0 0 0-.7 2.2 2.2 0 0 0 1.4-1.9zM7 13.6a1.5 1.5 0 1 1 1.5 1.5A1.5 1.5 0 0 1 7 13.6zm8.6 4.1a5.7 5.7 0 0 1-3.6 1.1 5.7 5.7 0 0 1-3.6-1.1.4.4 0 0 1 .6-.6 4.9 4.9 0 0 0 3 .9 4.9 4.9 0 0 0 3-.9.4.4 0 0 1 .6.6zm-.3-2.6a1.5 1.5 0 1 1 1.5-1.5 1.5 1.5 0 0 1-1.5 1.5z"></path></svg></a>
|
||
</div>
|
||
</div>
|
||
<nav class="foot-col" aria-label="Read">
|
||
<div class="eyebrow">Read</div>
|
||
<a href="/litepaper">Litepaper</a>
|
||
<a href="/litepaper#limits">What Igneum does not claim</a>
|
||
<a href="/litepaper#randomx">Igneum vs RandomX</a>
|
||
<a href="/bench">Engineering log</a>
|
||
<a href="/evidence">Evidence</a>
|
||
<a href="/ledger">Ledger: every criticism</a>
|
||
</nav>
|
||
<nav class="foot-col" aria-label="Run">
|
||
<div class="eyebrow">Run</div>
|
||
<a href="/miner">The miner</a>
|
||
<a href="/app">The app</a>
|
||
<a href="/wallet">The wallet</a>
|
||
<a href="/miners">GPU bench table</a>
|
||
<a href="/miner#fee">The dev fee</a>
|
||
<a href="/faucet">Testnet faucet</a>
|
||
</nav>
|
||
<nav class="foot-col" aria-label="Follow">
|
||
<div class="eyebrow">Follow</div>
|
||
<a href="/live">Live devnet</a>
|
||
<a href="/explorer">Explorer</a>
|
||
<a href="/litepaper#roadmap">Journey</a>
|
||
<a href="/metamask">Add to MetaMask</a>
|
||
<a href="/miner#get">Downloads: devnet build</a>
|
||
</nav>
|
||
<nav class="foot-col" aria-label="Contact">
|
||
<div class="eyebrow">Contact</div>
|
||
<a href="mailto:hello@igneum.network">hello@igneum.network</a>
|
||
<a href="https://github.com/igneum-network/spec/issues" rel="noopener">An issue on the spec</a>
|
||
<span class="foot-note">Report a flaw by either. Nobody from Igneum will ask for your seed.</span>
|
||
</nav>
|
||
</div>
|
||
<div class="foot-base">
|
||
<span class="foot-imprint">Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre</span>
|
||
<span class="foot-state"><span class="dot off" id="foot-dot"></span><span id="foot-state">devnet</span></span>
|
||
<span class="theme" role="group" aria-label="Theme"><button type="button" data-theme-set="system" aria-pressed="true">System</button><button type="button" data-theme-set="light" aria-pressed="false">Light</button><button type="button" data-theme-set="dark" aria-pressed="false">Dark</button></span>
|
||
<span class="mono foot-copy">© 2026 Igneum · not an offer to sell anything</span>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
<script>
|
||
(function(){
|
||
// theme control: stores light or dark under igneum-theme; system clears it. The head applied the stored value before paint.
|
||
var bs=document.querySelectorAll('[data-theme-set]');if(!bs.length)return;
|
||
function cur(){try{var t=localStorage.getItem('igneum-theme');return t==='light'||t==='dark'?t:'system';}catch(e){return 'system';}}
|
||
function paint(){var c=cur();bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});}
|
||
bs.forEach(function(b){b.addEventListener('click',function(){var v=b.getAttribute('data-theme-set');try{if(v==='system')localStorage.removeItem('igneum-theme');else localStorage.setItem('igneum-theme',v);}catch(e){}
|
||
if(v==='system')document.documentElement.removeAttribute('data-theme');else document.documentElement.setAttribute('data-theme',v);paint();});});
|
||
paint();
|
||
// the network state word in the footer: one read of the observer per page view
|
||
var fs=document.getElementById('foot-state'),fd=document.getElementById('foot-dot');if(fs&&fd){fetch('/api/live?window=30',{cache:'no-store'}).then(function(r){return r.json();}).then(function(d){var on=d&&d.ok&&d.state&&!d.state.stale;fs.textContent=on?'devnet live':'devnet, observer stale';fd.className='dot '+(on?'live':'off');}).catch(function(){fs.textContent='devnet';});}
|
||
})();
|
||
</script>
|
||
<!-- footer:end -->
|
||
|
||
<script src="/live-dag.js" defer></script>
|
||
<script src="/proof-core.js" defer></script>
|
||
<script>
|
||
(function(){
|
||
function $(id){return document.getElementById(id);}
|
||
var fmtN=function(v){return v===null||v===undefined?'pending':Number(v).toLocaleString('en-GB');};
|
||
function fmtHash(h){if(h===null||h===undefined||!(h>0))return 'pending';return h>=1e9?(h/1e9).toFixed(1)+' GH/s':(h/1e6).toFixed(1)+' MH/s';}
|
||
function rel(ms){var s=Math.max(0,Math.round(ms/1000));if(s<60)return s+' s ago';var m=Math.floor(s/60);if(m<60)return m+' min ago';var h=Math.floor(m/60);if(h<24)return h+' h ago';return Math.floor(h/24)+' d ago';}
|
||
function hms(ms){var d=new Date(ms);return String(d.getUTCHours()).padStart(2,'0')+':'+String(d.getUTCMinutes()).padStart(2,'0')+':'+String(d.getUTCSeconds()).padStart(2,'0');}
|
||
function esc(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');}
|
||
// a number that changed glows for a frame: motion only from a real change
|
||
function set(id,text){var el=$(id);if(!el||el.textContent===text)return;el.textContent=text;el.classList.add('flash');setTimeout(function(){el.classList.remove('flash');},60);}
|
||
var seen={},shardState={},cpState={},evSeen={},evList=[],ready=false,sel=null,lastD=null,dag=null;
|
||
var mine=(function(){try{return localStorage.getItem('igneum-my-key')||'';}catch(e){return '';}})();
|
||
document.addEventListener('click',function(e){var t=e.target;if(t&&t.id==='mine-btn'){var v=prompt('Your vote key’s short id, the first 8 hex characters shown in the app or the explorer:',mine||'');if(v!==null){mine=v.trim().toLowerCase();try{if(mine)localStorage.setItem('igneum-my-key',mine);else localStorage.removeItem('igneum-my-key');}catch(x){}remount();if(lastD)render(lastD);}}
|
||
var row=t&&t.closest?t.closest('tr[data-hash]'):null;if(row&&dag){dag.select(row.getAttribute('data-hash'),'table');}});
|
||
function render(d){var s=d.state,pv=d.proving,fn=d.finality,now=new Date(d.now).getTime();lastD=d;
|
||
// the six tiles
|
||
set('s-bps',s.blocks_per_second_60s!==null&&s.blocks_per_second_60s!==undefined?Number(s.blocks_per_second_60s).toFixed(2):'pending');
|
||
set('s-blue',fmtN(s.blue_score));set('s-hash',fmtHash(s.hashes_per_second_estimate));
|
||
set('s-keys',fmtN(s.miners_10m!==null&&s.miners_10m!==undefined?s.miners_10m:(d.miners||[]).length));set('s-peers',fmtN(s.peers));
|
||
var lock=null;if(fn&&fn.checkpoints)fn.checkpoints.forEach(function(c){if(c.state==='locked'&&(!lock||c.index>lock.index))lock=c;});
|
||
var le=$('s-lock');if(fn&&fn.supported&&!fn.active){set('s-lock',fn.latest_locked_index?'#'+fmtN(fn.latest_locked_index):'none');le.className='v tick';$('s-lock-d').textContent='finality paused: under two thirds of the weight is signing';}
|
||
else if(lock){set('s-lock','#'+fmtN(lock.index));le.className='v tick';var t=lock.locked_at?new Date(String(lock.locked_at).replace(' ','T')).getTime():NaN;$('s-lock-d').textContent=(isNaN(t)?'locked':'locked '+rel(now-t))+(lock.fraction_total?' with '+Math.round(lock.fraction_total*100)+'% of all 30-day weight':'');}
|
||
else{set('s-lock',fn&&fn.supported?'none yet':'no rule');le.className='v tick word';$('s-lock-d').textContent=fn&&fn.supported?'the 30-day weight window is filling':'';}
|
||
// the graph's count, the blue range, the chain-only view
|
||
var vr=dag?dag.getViewRange():null,w=dag?dag.getWindow():90,cut=vr?vr.start:now-w*1000,top0=vr?vr.end:now,on=[];(d.blocks||[]).forEach(function(b){if(b.ts>=cut&&b.ts<=top0)on.push(b);});
|
||
// block activity: the last 30 minutes, one bar a minute, from the observer's per-minute counts
|
||
var bpm=(s.blocks_per_minute||[]).slice(-30),max=Math.max(1,Math.max.apply(null,bpm.map(function(p){return p[1]||0;}).concat([1])));
|
||
$('a-count').textContent=on.length;$('a-bars').innerHTML=bpm.map(function(p,i){return '<i style="height:'+Math.max(2,Math.round((p[1]||0)/max*100))+'%" title="'+(p[1]||0)+' blocks"'+(i===bpm.length-1?' class="now"':'')+'></i>';}).join('');
|
||
var tot30=bpm.reduce(function(a,p){return a+(p[1]||0);},0);$('a-note').textContent=fmtN(tot30)+' blocks in the last '+bpm.length+' min, '+(bpm.length?(tot30/bpm.length).toFixed(1):'0')+' a minute on average; the target is 60.';
|
||
// mining identities: share of the displayed blocks, the reader's own key marked when known
|
||
var share={},tot=0;on.forEach(function(b){share[b.miner]=(share[b.miner]||0)+1;tot++;});var ids=Object.keys(share).sort(function(a,b){return share[b]-share[a];}),mel=$('miners');
|
||
if(ids.length){var top=ids.slice(0,8),rest=ids.length-top.length;mel.innerHTML=top.map(function(id){var n=share[id],pc=n/tot*100;return '<div class="id-row'+(mine&&id===mine?' mine':'')+'"><span class="name"><i class="sw"></i>'+esc(id)+(mine&&id===mine?' (you)':'')+'</span><span class="pct">'+pc.toFixed(1)+'%</span><div class="bar"><i style="width:'+Math.round(pc)+'%"></i></div></div>';}).join('');
|
||
$('miners-more').textContent=(rest>0?'and '+rest+' more. ':'')+'Vote-key share of the displayed blocks. Several keys may belong to one GPU.';}
|
||
else mel.innerHTML='<div class="empty">No blocks in the window.</div>';
|
||
// the event stream: the observer's own events plus what changed between reads
|
||
var evs=[];(d.events||[]).forEach(function(e){evs.push({ts:new Date(e.ts).getTime(),k:e.kind.replace(/_/g,' '),cls:e.kind==='checkpoint_locked'?'lock':/quiet/.test(e.kind)?'quiet':'',text:e.text});});
|
||
(d.blocks||[]).forEach(function(b){if(!seen[b.hash]){seen[b.hash]=1;if(ready)evs.push({ts:b.ts,k:'Block observed',cls:'',text:(b.number?'#'+fmtN(b.number):b.hash)+' by '+b.miner});}
|
||
(b.shards||[]).forEach(function(sh){var key=b.hash+':'+sh.i,prev=shardState[key];shardState[key]=sh.state;if(ready&&prev!==sh.state&&(sh.state==='verified'||sh.state==='paid'))evs.push({ts:now,k:sh.state==='paid'?'Shard paid':'Shard proven',cls:'',text:(sh.i+1)+' of '+sh.n+' for '+(b.number?'#'+fmtN(b.number):b.hash)+(sh.prover?' by '+sh.prover:'')});});});
|
||
if(fn&&fn.checkpoints)fn.checkpoints.forEach(function(c){var prev=cpState[c.index];cpState[c.index]=c.state;if(ready&&prev!==c.state)evs.push({ts:c.state==='locked'&&c.locked_at?new Date(String(c.locked_at).replace(' ','T')).getTime():now,k:'Checkpoint '+c.state,cls:c.state==='locked'?'lock':'',text:'#'+fmtN(c.index)+(c.fraction_total?', '+Math.round(c.fraction_total*100)+'% of weight':'')});});
|
||
evs.forEach(function(e){var id=e.k+'|'+e.text+'|'+Math.round(e.ts/1000);if(!evSeen[id]){evSeen[id]=1;evList.unshift({id:id,fresh:ready,ts:e.ts,k:e.k,cls:e.cls,text:e.text});}});
|
||
evList.sort(function(a,b){return b.ts-a.ts;});evList=evList.slice(0,14);ready=true;
|
||
var eel=$('events');if(evList.length){eel.innerHTML=evList.map(function(e){var f=e.fresh;e.fresh=false;return '<div'+(f?' class="new"':'')+'><span class="dot '+e.cls+'"></span><span class="what"><b>'+esc(e.k)+'</b> '+esc(e.text)+'</span><span class="t">'+hms(e.ts)+'</span></div>';}).join('');$('ev-count').textContent=evList.length+' recent';}
|
||
// the table: the displayed blocks, newest first
|
||
var rows=on.slice().sort(function(a,b){return b.blue_score-a.blue_score;}).slice(0,40);
|
||
$('tbody').innerHTML=rows.length?rows.map(function(b){var word=b.locked?'locked checkpoint':b.proven?'proven':b.color==='red'?'excluded':b.chain?'selected chain':b.color==='blue'?'included':'pending';return '<tr data-hash="'+esc(b.hash)+'"><td class="num">'+fmtN(b.blue_score)+'</td><td>'+esc(b.hash)+'</td><td>'+esc(b.miner)+'</td><td class="num">'+(b.parents||[]).length+'</td><td>'+word+'</td><td class="num">'+(b.number?fmtN(b.number):'')+'</td></tr>';}).join(''):'<tr><td colspan="6">No blocks in the window.</td></tr>';
|
||
}
|
||
// the inspector: the pack's selected record, kept fresh on every reply through onSelect(block, 'update'); the proof scene is IgneumProof
|
||
var proof=null;
|
||
function inspect(b){sel=b;$('insp-close').hidden=false;var mineB=mine&&b.miner===mine;$('i-kind').textContent=mineB?'your block':b.locked?'checkpoint':b.chain?'chain block':b.color==='red'?'excluded':'side block';
|
||
$('i-title').textContent=b.locked?'Checkpoint locked':b.proven?'Proof complete':(b.shards||[]).some(function(x){return x.state==='proving';})?'Proof in progress':'Block observed';
|
||
$('i-hash').textContent=b.hash+(b.number?' · #'+fmtN(b.number):'');
|
||
var sh=b.shards||[],done=sh.filter(function(x){return x.state==='verified'||x.state==='paid';}).length;
|
||
$('i-shard-count').textContent=sh.length?done+' / '+sh.length+' complete':'no shard plan yet';$('i-track').innerHTML=sh.map(function(x){return '<i class="'+esc(x.state)+'"></i>';}).join('');
|
||
$('i-shards').innerHTML=sh.length?sh.map(function(x,i){return '<li class="'+esc(x.state)+'"><span>Shard '+String(i+1).padStart(2,'0')+'</span><span>'+esc(x.state.charAt(0).toUpperCase()+x.state.slice(1))+'</span></li>';}).join(''):'<li class="muted">'+(b.chain?'No shard plan yet.':'Not a chain block: no shards.')+'</li>';
|
||
$('i-incl').textContent=(b.color==='blue'?'Included':b.color==='red'?'Excluded':'Pending')+(b.chain?' / selected chain':'');
|
||
$('i-miner').textContent=b.miner+(mineB?' (you)':'');$('i-blue').textContent=fmtN(b.blue_score);$('i-daa').textContent=fmtN(b.daa);
|
||
$('i-parents').textContent=(b.parents||[]).length+' observed'+((b.parents||[]).length?': '+b.parents.join(', '):'');$('i-time').textContent=hms(b.ts)+' UTC';
|
||
var fn=lastD&&lastD.finality,cp=null;if(fn&&fn.checkpoints)fn.checkpoints.forEach(function(c){if(b.locked&&c.hash===b.hash)cp=c;});
|
||
$('i-cp').textContent=cp?'this block, #'+fmtN(cp.index)+(cp.fraction_total?', '+Math.round(cp.fraction_total*100)+'% of weight':''):'not a checkpoint';
|
||
$('i-lock').textContent=b.final?'Final (reported)':'Not marked final';
|
||
if(proof)proof.setBlock(b);}
|
||
function uninspect(){sel=null;$('insp-close').hidden=true;$('i-kind').textContent='select a block';$('i-title').textContent='Select a block';$('i-hash').textContent='Click the graph, or a row of the table below.';$('i-shard-count').textContent='not set';$('i-track').innerHTML='';$('i-shards').innerHTML='<li class="muted">No block selected.</li>';['i-incl','i-miner','i-blue','i-daa','i-parents','i-time','i-cp','i-lock'].forEach(function(id){$(id).textContent='not set';});if(proof)proof.setBlock(null);}
|
||
$('insp-close').addEventListener('click',function(){if(dag)dag.select(null,'clear');uninspect();});
|
||
// the four stats above the graph, from the module's own window
|
||
function miner(st){if(!dag)return;var cps=dag.getCheckpoints(),cp=cps.length?cps[cps.length-1]:null;
|
||
$('m-blocks').textContent=fmtN(st.blocks);$('m-win').textContent='last '+st.window+' s';$('m-keys').textContent=st.minerKeys+' miner keys · not a card count';$('lane-caption').textContent=st.minerKeys+' keys';
|
||
$('m-mine').textContent=mine?fmtN(st.own):'0';$('m-mine').classList.toggle('dim',!mine);$('m-key').textContent=mine||'not set';
|
||
$('m-proven').textContent=fmtN(st.proven);$('m-proven-pct').textContent=st.blocks?Math.round(st.proven/st.blocks*100)+'% of window':'no blocks';
|
||
$('m-cp').textContent=cp?(cp.state==='locked'?'Locked':'Pending'):'None';$('m-cp-w').textContent=cp&&cp.fraction_total!==null&&cp.fraction_total!==undefined?Math.round(cp.fraction_total*100)+'% weight':'';$('m-cp-note').textContent=cp?'#'+fmtN(cp.index)+' · blue score '+fmtN(cp.blue_score):'no checkpoint in the reply';
|
||
$('c-count').textContent=st.blocks+' blocks';var r=dag.getViewRange(),lo=null,hi=null;dag.getBlocks().forEach(function(b){if(b.ts>=r.start&&b.ts<=r.end){if(lo===null||b.blue_score<lo)lo=b.blue_score;if(hi===null||b.blue_score>hi)hi=b.blue_score;}});$('blue-range').textContent=lo===null?'blue score pending':'blue score '+fmtN(lo)+' to '+fmtN(hi);}
|
||
function mountDag(){if(dag||!window.IgneumDag)return;
|
||
dag=IgneumDag.mount($('dag'),{poll:false,window:60,fps:60,mine:mine||undefined,tooltip:$('dag-tip'),chip:$('dag-chip'),pauseButton:$('dag-pause'),
|
||
onWindow:function(w){$('z-pct').textContent=w+' s';if(lastD)render(lastD);},
|
||
onState:function(st,reason){var el=$('dag-state');el.className='state '+(st==='live'?'live':st==='failed'?'bad':'');el.innerHTML='<span class="dot '+(st==='live'?'live':st==='failed'?'bad':'off')+'"></span>'+(st==='live'?'live devnet':esc(reason||st));if(proof)proof.setPaused(st!=='live'||dag.getStats().paused);},
|
||
onPause:function(p){$('dag-follow').classList.toggle('on',!p);if(proof)proof.setPaused(p||dag.getState().state!=='live');},
|
||
onFollow:function(f){$('dag-follow').classList.toggle('on',f);},
|
||
onStats:miner,
|
||
onSelect:function(b,why){if(b){var full=lastD&&(lastD.blocks||[]).filter(function(x){return x.hash===b.hash;})[0];inspect(Object.assign({},b,full?{number:full.number}:{}));}else uninspect();}});
|
||
if(window.IgneumProof&&!proof)proof=IgneumProof.mount($('proof'));
|
||
if(lastD)feed(lastD);}
|
||
function remount(){if(dag){dag.destroy();dag=null;}mountDag();}
|
||
function feed(d){if(!dag)return;dag.push(d);}
|
||
if(window.IgneumDag)mountDag();else{var ms=document.querySelector('script[src="/live-dag.js"]');if(ms)ms.addEventListener('load',mountDag);}
|
||
if(!window.IgneumProof){var ps=document.querySelector('script[src="/proof-core.js"]');if(ps)ps.addEventListener('load',function(){if(dag&&!proof)proof=IgneumProof.mount($('proof'));});}
|
||
document.querySelectorAll('.tab[data-filter]').forEach(function(t){t.addEventListener('click',function(){document.querySelectorAll('.tab[data-filter]').forEach(function(x){x.classList.toggle('on',x===t);x.setAttribute('aria-pressed',x===t?'true':'false');});if(dag)dag.setFilter(t.getAttribute('data-filter'));});});
|
||
$('dag-follow').addEventListener('click',function(){if(dag){dag.setPaused(false);dag.follow();}});
|
||
$('z-in').addEventListener('click',function(){if(dag)dag.setWindow(dag.getWindow()-15);});$('z-out').addEventListener('click',function(){if(dag)dag.setWindow(dag.getWindow()+15);});
|
||
window.__dbg={render:render,feed:feed,getDag:function(){return dag;}};
|
||
function setLive(st,reason){var el=$('live-state');el.className='state '+(st==='live'?'live':st==='failed'?'bad':'');el.innerHTML='<span class="dot '+(st==='live'?'live':st==='failed'?'bad':'off')+'"></span>'+(st==='live'?'live devnet · one node read every 2 s':st==='connecting'?'connecting to the observer':esc(reason||st));}
|
||
function poll(){fetch('/api/live?window=300',{cache:'no-store'}).then(function(r){if(!r.ok)throw new Error(r.status);return r.json();}).then(function(d){if(!d.ok)throw new Error(d.error);failures=0;setLive(d.state.stale?'stale':'live',d.state.stale?'observer stale':'');render(d);feed(d);}).catch(function(){failures++;if(failures>=3)setLive('failed','live feed unavailable');});}
|
||
poll();setInterval(function(){if(!document.hidden)poll();},2000);
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|