blockyard 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +929 -0
- package/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +191 -4
- package/SECURITY.md +38 -0
- package/bin/blockyard.js +41 -0
- package/config/pool-map.json +2620 -0
- package/docs/API.md +1577 -0
- package/docs/ARCHITECTURE.md +1394 -0
- package/docs/AUTO-UPDATE.md +269 -0
- package/docs/CONFIGURATION.md +847 -0
- package/docs/DEFECTS.md +813 -0
- package/docs/EFFECTS-AGENTS.md +448 -0
- package/docs/GETTING-STARTED.md +205 -0
- package/docs/INSTALL.md +547 -0
- package/docs/MEASUREMENTS.md +1401 -0
- package/docs/RULES.md +681 -0
- package/docs/SECURITY-AUDIT-2026-09-14.md +177 -0
- package/docs/SECURITY-AUDIT.md +258 -0
- package/docs/SECURITY.md +212 -0
- package/docs/TROUBLESHOOTING.md +332 -0
- package/docs/USER-GUIDE.md +1262 -0
- package/package.json +53 -5
- package/public/404.html +9 -0
- package/public/css/app.css +2009 -0
- package/public/donate-qr.png +0 -0
- package/public/index.html +1085 -0
- package/public/js/about.js +112 -0
- package/public/js/agents.js +1141 -0
- package/public/js/app.js +1386 -0
- package/public/js/arkanoid.js +806 -0
- package/public/js/blockanoid.js +347 -0
- package/public/js/blockout.js +347 -0
- package/public/js/blockpack.js +428 -0
- package/public/js/blockscene3d.js +2830 -0
- package/public/js/breakout.js +224 -0
- package/public/js/charts.js +635 -0
- package/public/js/depthchart.js +315 -0
- package/public/js/details3d.js +4342 -0
- package/public/js/doom.js +31 -0
- package/public/js/dosaudio.js +48 -0
- package/public/js/dosgame.js +389 -0
- package/public/js/dosio.js +186 -0
- package/public/js/dospc.js +1353 -0
- package/public/js/dosworker.js +196 -0
- package/public/js/explorer.js +405 -0
- package/public/js/feepalette.js +149 -0
- package/public/js/fmt.js +162 -0
- package/public/js/goggles.js +886 -0
- package/public/js/kiosk.js +41 -0
- package/public/js/login.js +88 -0
- package/public/js/markets.js +395 -0
- package/public/js/mining.js +1416 -0
- package/public/js/panels.js +970 -0
- package/public/js/pricechart.js +189 -0
- package/public/js/quake.js +20 -0
- package/public/js/settings.js +1096 -0
- package/public/js/soundcard.js +459 -0
- package/public/js/tetris.js +226 -0
- package/public/js/tetrust.js +356 -0
- package/public/js/tetsound.js +175 -0
- package/public/js/theme.js +235 -0
- package/public/js/wolf3d.js +22 -0
- package/public/js/x86.js +1978 -0
- package/public/login.html +33 -0
- package/scripts/blockfile-measure.js +156 -0
- package/scripts/browser-check.mjs +286 -0
- package/scripts/check.js +173 -0
- package/scripts/decode-check.js +81 -0
- package/scripts/doc-counts.js +109 -0
- package/scripts/donate-qr.py +23 -0
- package/scripts/dos-bench.js +56 -0
- package/scripts/fake-node.js +534 -0
- package/scripts/index-bench.js +216 -0
- package/scripts/index-benchmark.js +117 -0
- package/scripts/index-build.js +40 -0
- package/scripts/live-render-check.mjs +89 -0
- package/scripts/manage-users.js +132 -0
- package/scripts/motion-check.mjs +138 -0
- package/scripts/pool-map.js +157 -0
- package/scripts/setup.js +432 -0
- package/scripts/shots.mjs +278 -0
- package/scripts/smoke.sh +327 -0
- package/scripts/tls.js +31 -0
- package/scripts/ui.js +174 -0
- package/server/auth/sessions.js +221 -0
- package/server/auth/users.js +243 -0
- package/server/chain/blockfile.js +234 -0
- package/server/chain/index/build.js +210 -0
- package/server/chain/index/heights.js +36 -0
- package/server/chain/index/live.js +276 -0
- package/server/chain/index/rows.js +145 -0
- package/server/chain/index/store.js +154 -0
- package/server/chain/index/worker.js +109 -0
- package/server/chain/tx.js +310 -0
- package/server/collect/gbt.js +229 -0
- package/server/collect/logparse.js +765 -0
- package/server/collect/logtail.js +189 -0
- package/server/collect/markets.js +333 -0
- package/server/collect/mining.js +333 -0
- package/server/collect/monitor.js +2545 -0
- package/server/collect/network.js +295 -0
- package/server/collect/nextblock.js +275 -0
- package/server/collect/sync.js +386 -0
- package/server/config.js +644 -0
- package/server/http/api.js +1319 -0
- package/server/http/explorer.js +418 -0
- package/server/http/games.js +77 -0
- package/server/http/server.js +420 -0
- package/server/http/sse.js +176 -0
- package/server/http/static.js +212 -0
- package/server/main.js +673 -0
- package/server/netinfo.js +253 -0
- package/server/rpc/allowlist.js +130 -0
- package/server/rpc/client.js +414 -0
- package/server/store/audit.js +148 -0
- package/server/store/history.js +220 -0
- package/server/store/ledger.js +290 -0
- package/server/store/ring.js +173 -0
- package/server/tls/selfsigned.js +160 -0
- package/server/util/fmt.js +29 -0
- package/systemd/blockyard.service +102 -0
|
@@ -0,0 +1,1085 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<!-- data-blockyard-build is rewritten per response by server/http/static.js, so the page
|
|
3
|
+
can compare the build it was served against what /api/build says is current and
|
|
4
|
+
say "this tab is stale" out loud. `%BLOCKYARD_BUILD%` is the placeholder. -->
|
|
5
|
+
<html lang="en" data-blockyard-build="%BLOCKYARD_BUILD%">
|
|
6
|
+
<head>
|
|
7
|
+
<meta charset="utf-8">
|
|
8
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
9
|
+
<meta name="referrer" content="no-referrer">
|
|
10
|
+
<title>BlockYard - Bitcoin node monitor</title>
|
|
11
|
+
<link rel="stylesheet" href="/css/app.css">
|
|
12
|
+
<link rel="icon" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2032%2032%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22g%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23ffb85c%22%2F%3E%3Cstop%20offset%3D%22.55%22%20stop-color%3D%22%23f7931a%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23a8640d%22%2F%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Crect%20width%3D%2232%22%20height%3D%2232%22%20rx%3D%227.5%22%20fill%3D%22url%28%23g%29%22%2F%3E%3Cg%20fill%3D%22%2316110a%22%20opacity%3D%22.14%22%3E%3Crect%20x%3D%224%22%20y%3D%2224.6%22%20width%3D%228%22%20height%3D%223.6%22%20rx%3D%221%22%2F%3E%3Crect%20x%3D%2213%22%20y%3D%2224.6%22%20width%3D%226%22%20height%3D%223.6%22%20rx%3D%221%22%2F%3E%3Crect%20x%3D%2220%22%20y%3D%2224.6%22%20width%3D%228%22%20height%3D%223.6%22%20rx%3D%221%22%2F%3E%3C%2Fg%3E%3Cpath%20fill%3D%22%231a1206%22%20fill-rule%3D%22evenodd%22%20d%3D%22M5%208h6.6a4.2%204.2%200%200%201%202.35%207.66A4.4%204.4%200%200%201%2012%2024H5Zm3.3%202.9v3.5h3.1a1.75%201.75%200%200%200%200-3.5Zm0%206.4v3.8h3.4a1.9%201.9%200%200%200%200-3.8Z%22%2F%3E%3Cpath%20fill%3D%22%231a1206%22%20d%3D%22M16.8%208h3.3l2.75%205.3L25.6%208h3.3l-4.85%208.8V24h-3.3v-7.2Z%22%2F%3E%3C%2Fsvg%3E">
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div class="app">
|
|
16
|
+
<header class="top">
|
|
17
|
+
<!-- The monogram IS the About link (operator: "Make clicking the BlockYard icon the About
|
|
18
|
+
page then"). A <button data-page> rather than a bare click handler, so it satisfies the
|
|
19
|
+
same page contract every nav tab does: test/web-contract.test.js holds every
|
|
20
|
+
`<button data-page>` to a matching <section> and a render case. -->
|
|
21
|
+
<button type="button" data-page="about" class="brand" id="brandAbout" title="About BlockYard" aria-label="About BlockYard"><svg class="mark" viewBox="0 0 32 32" role="img" aria-label="BlockYard">
|
|
22
|
+
<defs><linearGradient id="byTile" x1="0" y1="0" x2="1" y2="1">
|
|
23
|
+
<stop offset="0" stop-color="#ffb85c"/><stop offset=".55" stop-color="#f7931a"/><stop offset="1" stop-color="#a8640d"/>
|
|
24
|
+
</linearGradient></defs>
|
|
25
|
+
<rect x=".5" y=".5" width="31" height="31" rx="7.5" fill="url(#byTile)"/>
|
|
26
|
+
<g fill="#16110a" opacity=".14"><rect x="4" y="24.6" width="8" height="3.6" rx="1"/><rect x="13" y="24.6" width="6" height="3.6" rx="1"/><rect x="20" y="24.6" width="8" height="3.6" rx="1"/></g>
|
|
27
|
+
<path fill="#1a1206" fill-rule="evenodd" d="M5 8h6.6a4.2 4.2 0 0 1 2.35 7.66A4.4 4.4 0 0 1 12 24H5Zm3.3 2.9v3.5h3.1a1.75 1.75 0 0 0 0-3.5Zm0 6.4v3.8h3.4a1.9 1.9 0 0 0 0-3.8Z"/>
|
|
28
|
+
<path fill="#1a1206" d="M16.8 8h3.3l2.75 5.3L25.6 8h3.3l-4.85 8.8V24h-3.3v-7.2Z"/>
|
|
29
|
+
</svg>
|
|
30
|
+
<span class="wordmark">Block<b>Yard</b></span> <small id="ver"></small></button>
|
|
31
|
+
<nav class="pages" id="nav">
|
|
32
|
+
<button data-page="overview" class="on">Overview</button>
|
|
33
|
+
<button data-page="space">Block space</button>
|
|
34
|
+
<button data-page="chain">Chain & Sync</button>
|
|
35
|
+
<button data-page="mempool">Mempool</button>
|
|
36
|
+
<button data-page="explorer">Explorer</button>
|
|
37
|
+
<button data-page="markets">Markets</button>
|
|
38
|
+
<button data-page="kiosk">Kiosk</button>
|
|
39
|
+
<button data-page="peers">Peers</button>
|
|
40
|
+
<button data-page="network">Network</button>
|
|
41
|
+
<button data-page="mining">Mining</button>
|
|
42
|
+
<button data-page="logs">Events</button>
|
|
43
|
+
<button data-page="node">Node & RPC</button>
|
|
44
|
+
<button data-page="admin" id="navAdmin" hidden>Admin</button>
|
|
45
|
+
</nav>
|
|
46
|
+
<!-- DIVERSIONS (operator, 2026-09-12: "Move Tetrust and Blockout to the very end of the menu
|
|
47
|
+
options under a pop-down menu called 'Diversions'"). The toggle deliberately carries no
|
|
48
|
+
data-page, because a nav button with one is required to have a page section behind it.
|
|
49
|
+
OUTSIDE <nav>, and that is the whole point (operator, Safari on macOS: "still doesn't work
|
|
50
|
+
on safari. not clickable"). It used to sit inside `nav.pages`, which is a horizontal
|
|
51
|
+
scroll container, held at the right edge with `position: sticky; right: 0`. WebKit paints
|
|
52
|
+
a sticky element in its stuck position but hit-tests it where layout originally put it, so
|
|
53
|
+
the button was plainly visible and completely dead to the pointer -- in that engine only,
|
|
54
|
+
which is why every check here passed. Out here it is an ordinary flex item in the header
|
|
55
|
+
row: no sticky, no scroll container, nothing to go wrong. Being outside <nav> also means
|
|
56
|
+
the nav's delegated click handler no longer reaches the items, so app.js routes them from
|
|
57
|
+
the panel's own handler. -->
|
|
58
|
+
<div class="navmenu" id="navDiv">
|
|
59
|
+
<button type="button" class="navmenu-btn" id="navDivBtn" aria-haspopup="true" aria-expanded="false">Diversions <span class="navcaret">▾</span></button>
|
|
60
|
+
</div>
|
|
61
|
+
<span class="spacer"></span>
|
|
62
|
+
<div class="meta">
|
|
63
|
+
<span id="nodePick"></span>
|
|
64
|
+
<span title="Server-sent events link">stream <b id="sseState" class="warn">connecting</b></span>
|
|
65
|
+
<span title="Node RPC round trip">rpc <b id="rpcLat">–</b></span>
|
|
66
|
+
<span title="Monitor uptime">up <b id="appUp">–</b></span>
|
|
67
|
+
<!-- Shown only when the server reports accounts=false. The posture has to be
|
|
68
|
+
visible in the page, not inferred from a sign-out button that is missing. -->
|
|
69
|
+
<span id="accessPill" class="hidden"><b class="badge muted">open access</b></span>
|
|
70
|
+
<!-- Only shown when the loaded build differs from the served one. Its whole
|
|
71
|
+
purpose is the case where nothing else on screen has changed. -->
|
|
72
|
+
<span id="buildNote" class="hidden"><b class="badge bad">stale build</b></span>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="userchip">
|
|
75
|
+
<span class="tiny faint" id="whoami"></span>
|
|
76
|
+
<button class="btn gear" id="btnSettings" title="Display settings" aria-label="Display settings" aria-expanded="false">
|
|
77
|
+
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true" focusable="false" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"><path d="M 13.94 2 c 0.50 0 0.92 0.37 0.99 0.87 l 0.26 1.85 c 0.50 0.18 0.97 0.41 1.40 0.69 l 1.73 -0.72 c 0.45 -0.19 0.98 -0.02 1.25 0.43 l 1.44 2.50 c 0.26 0.45 0.16 1.02 -0.24 1.28 l -1.47 1.16 c 0.04 0.28 0.06 0.56 0.06 0.84 c 0 0.28 -0.02 0.56 -0.06 0.84 l 1.47 1.16 c 0.40 0.26 0.50 0.83 0.24 1.28 l -1.44 2.50 c -0.27 0.45 -0.80 0.62 -1.25 0.43 l -1.73 -0.72 c -0.43 0.28 -0.90 0.51 -1.40 0.69 l -0.26 1.85 c -0.07 0.50 -0.49 0.87 -0.99 0.87 h -2.88 c -0.50 0 -0.92 -0.37 -0.99 -0.87 l -0.26 -1.85 c -0.50 -0.18 -0.97 -0.41 -1.40 -0.69 l -1.73 0.72 c -0.45 0.19 -0.98 0.02 -1.25 -0.43 l -1.44 -2.50 c -0.26 -0.45 -0.16 -1.02 0.24 -1.28 l 1.47 -1.16 c -0.04 -0.28 -0.06 -0.56 -0.06 -0.84 c 0 -0.28 0.02 -0.56 0.06 -0.84 l -1.47 -1.16 c -0.40 -0.26 -0.50 -0.83 -0.24 -1.28 l 1.44 -2.50 c 0.27 -0.45 0.80 -0.62 1.25 -0.43 l 1.73 0.72 c 0.43 -0.28 0.90 -0.51 1.40 -0.69 l 0.26 -1.85 c 0.07 -0.50 0.49 -0.87 0.99 -0.87 z M 12 8.40 a 3.60 3.60 0 1 0 0 7.20 a 3.60 3.60 0 0 0 0 -7.20 z"/></svg>
|
|
78
|
+
</button>
|
|
79
|
+
<button class="btn" id="btnPause" title="Freeze live updates (hold Shift to also stop the event feed)">pause</button>
|
|
80
|
+
<button class="btn" id="btnLogout">sign out</button>
|
|
81
|
+
</div>
|
|
82
|
+
</header>
|
|
83
|
+
|
|
84
|
+
<!-- THE DIVERSIONS PANEL, a direct child of <body> (operator, Safari on macOS: "rendering on
|
|
85
|
+
safari is still broken. It's only showing half the drop-down contents").
|
|
86
|
+
It used to sit inside `header.top`, which is `overflow: hidden` and `height: 46px`. The panel
|
|
87
|
+
opens ~32px down and stands ~88px tall, so everything past the header's 46px was cut -- about
|
|
88
|
+
half of it, in WebKit. `position: fixed` is supposed to escape an ancestor's overflow, and in
|
|
89
|
+
Chrome and Firefox it did, which is why this looked fine everywhere it was tested.
|
|
90
|
+
The settings dialog (.cfgwrap) is the proof of the right shape: also fixed, also an overlay,
|
|
91
|
+
and it renders correctly in Safari. It sits out here at the top level of .app rather than
|
|
92
|
+
inside the header, and so does this panel now -- .app is a plain grid with no overflow, no
|
|
93
|
+
transform and no filter, so nothing between this element and the viewport can clip it or
|
|
94
|
+
become its containing block. (It is NOT a child of <body>: the page is wrapped in .app. The
|
|
95
|
+
invariant that matters is "not inside the clipped header", which is what the test pins.)
|
|
96
|
+
Its button stays in the header; only the panel moved, so app.js positions it from that
|
|
97
|
+
button's rect and routes the games from here. -->
|
|
98
|
+
<div class="navmenu-pop hidden" id="navDivPop" role="menu">
|
|
99
|
+
<button data-page="tetrust" role="menuitem" title="Tetrust: trust, but verify">Tetrust</button>
|
|
100
|
+
<button data-page="blockout" role="menuitem" title="Blockout: break the wall">Blockout</button>
|
|
101
|
+
<button data-page="blockanoid" role="menuitem" title="Blockanoid: Vaus, capsules and the minions">Blockanoid</button>
|
|
102
|
+
<button data-page="wolf3d" role="menuitem" title="Wolfenstein 3D: the shareware WOLF3D.EXE on an emulated PC">Wolfenstein 3D</button>
|
|
103
|
+
<button data-page="doom" role="menuitem" title="DOOM: the shareware DOOM.EXE on an emulated 486">DOOM</button>
|
|
104
|
+
<button data-page="quake" role="menuitem" title="Quake: the shareware QUAKE.EXE on an emulated PC">Quake</button>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<!-- Display settings (settings.js). Hidden until the gear is pressed; the contents are
|
|
108
|
+
rendered from settings.js PANEL, so a value and its control cannot drift apart. -->
|
|
109
|
+
<!-- "View more »" on the Mining tab's cards (2026-09-15): the same card, full screen, drawn by
|
|
110
|
+
mining.js renderExpand while it is open; Esc, the scrim or the close button shut it. -->
|
|
111
|
+
<div class="expandwrap hidden" id="expandWrap" role="dialog" aria-modal="true" aria-labelledby="expandTitle">
|
|
112
|
+
<div class="cfgscrim" id="expandScrim"></div>
|
|
113
|
+
<section class="expand card">
|
|
114
|
+
<h3><span id="expandTitle">…</span> <span class="sp"></span><span class="src" id="expandSrc"></span> <button type="button" class="btn" id="expandClose" aria-label="Close">close</button></h3>
|
|
115
|
+
<div class="expandbody" id="expandBody"></div>
|
|
116
|
+
</section>
|
|
117
|
+
</div>
|
|
118
|
+
<div class="cfgwrap hidden" id="settingsWrap">
|
|
119
|
+
<div class="cfgscrim" id="settingsScrim"></div>
|
|
120
|
+
<section class="cfg" id="settingsPanel" role="dialog" aria-modal="true" aria-labelledby="cfgTitle">
|
|
121
|
+
<h2 id="cfgTitle">Display settings <span class="sp"></span>
|
|
122
|
+
<button class="btn" id="cfgReset" title="Put every setting back to the shipped default">reset</button>
|
|
123
|
+
<button class="btn" id="cfgClose" aria-label="Close settings">close</button>
|
|
124
|
+
</h2>
|
|
125
|
+
<p class="cfgnote">Saved on the server, so every screen sees the same board; applied as you change them. They change what is drawn, never what is measured.</p>
|
|
126
|
+
<div id="cfgBody"></div>
|
|
127
|
+
</section>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<main>
|
|
131
|
+
<div id="offline" class="offline-bar hidden"></div>
|
|
132
|
+
|
|
133
|
+
<!-- ======================================================== OVERVIEW -->
|
|
134
|
+
<section class="page on" data-page="overview">
|
|
135
|
+
<div class="grid">
|
|
136
|
+
<div data-sync-hero="overview"></div>
|
|
137
|
+
|
|
138
|
+
<!-- The markets summary, between the sync strip and Block flow (operator, 2026-09-13:
|
|
139
|
+
"We really need to squeeze this line into the top of the Overview"). Hidden unless
|
|
140
|
+
markets.overviewSummary is on: the figures need the exchange feed, and Overview is
|
|
141
|
+
the landing page -- see the note on that setting. Same class as the Markets page's
|
|
142
|
+
own line, and filled by the same summaryHtml(), so the two cannot drift. -->
|
|
143
|
+
<div class="mksum ovmksum" id="ovMkSummary" hidden></div>
|
|
144
|
+
|
|
145
|
+
<!-- Two independent stacks (operator, 2026-09-11: "Still too much empty
|
|
146
|
+
space here. Need to compress the mempool and fees view at the very
|
|
147
|
+
least to sit side-by-side"). A shared grid row coupled the columns,
|
|
148
|
+
so whichever side was taller padded the other; here each column
|
|
149
|
+
stacks on its own, and the last card of each takes the height that
|
|
150
|
+
is left and scrolls inside it. -->
|
|
151
|
+
<div class="ovcols">
|
|
152
|
+
<div class="ovcol">
|
|
153
|
+
<div class="card w8 flowtight">
|
|
154
|
+
<h3>Block flow <span class="sp"></span><span class="src">being assembled → <b>the current block</b> → history · <a href="#mining">packages & goggles</a></span></h3>
|
|
155
|
+
<div id="ovTrain" class="flowwrap"></div>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<div class="ovpair">
|
|
159
|
+
<div class="card w5" id="ovMempoolCard">
|
|
160
|
+
<h3>Mempool <span class="sp"></span><span class="src">getmempoolinfo · vs blocks mined</span></h3>
|
|
161
|
+
<div class="metric"><div class="l">transactions</div><div><span class="v" id="ovMpCount">–</span><span class="u">tx</span></div>
|
|
162
|
+
<div class="s" id="ovMpBytes">–</div></div>
|
|
163
|
+
<canvas class="chart xs" id="ovMpChart"></canvas>
|
|
164
|
+
<div class="s faint" id="ovMpVsBlock">–</div>
|
|
165
|
+
<div class="kv" id="ovMpVsBlockKv"></div>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<div class="card w3 compact" id="ovFeesCard">
|
|
169
|
+
<h3>Fees <span class="sp"></span><span class="src">estimatesmartfee</span></h3>
|
|
170
|
+
<div class="kv" id="ovFees"><dt>–</dt></div>
|
|
171
|
+
<canvas class="chart xs" id="ovFeeChart"></canvas>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<div class="card w8" id="ovBlocksCard">
|
|
176
|
+
<h3>Last blocks <span class="sp"></span><span class="src">getblockstats + [block] stored</span></h3>
|
|
177
|
+
<div class="scroll md">
|
|
178
|
+
<table class="t" id="ovBlocks">
|
|
179
|
+
<thead><tr><th>height</th><th>age</th><th>gap</th><th class="r">txs</th><th class="r">size</th><th class="r">vsize</th><th class="r">fees (btc)</th><th class="r">min fee rate</th><th>from peer</th></tr></thead>
|
|
180
|
+
<tbody></tbody>
|
|
181
|
+
</table>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
<div class="ovcol">
|
|
187
|
+
<div class="card w8">
|
|
188
|
+
<h3>Block space <span class="sp"></span><span class="src">area = vbytes, colour = feerate paid</span></h3>
|
|
189
|
+
<div class="treemapwrap sq">
|
|
190
|
+
<canvas class="treemap" id="ovGnTreemap"></canvas><div class="viewer-ctl"><div class="refresh-in" data-refresh></div><button type="button" class="refresh-now" data-refresh-now disabled>refresh now</button></div>
|
|
191
|
+
<div class="goggles-tip hidden"></div>
|
|
192
|
+
</div>
|
|
193
|
+
<div class="note tiny faint" id="ovGnTreemapNote"></div>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<div class="card w4" id="ovEventsCard">
|
|
197
|
+
<h3>Monitor events <span class="sp"></span><span class="src">what this monitor observed; not node log lines</span></h3>
|
|
198
|
+
<div class="feed" id="ovFeed"></div>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
<div class="card w12">
|
|
204
|
+
<h3>Mined by <span class="sp"></span><span class="src">coinbase text + curated labels</span></h3>
|
|
205
|
+
<div id="ovMiningPools"></div>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<div class="card w12" id="ovCaveats"></div>
|
|
209
|
+
</div>
|
|
210
|
+
</section>
|
|
211
|
+
|
|
212
|
+
<!-- ===================================================== BLOCK SPACE -->
|
|
213
|
+
<!-- The viewer at full size, with the block being built and the chain tip
|
|
214
|
+
beside it (operator, 2026-09-11: "blockspace viewer really deserves its
|
|
215
|
+
own tab so it can run in a large window. Try to incorporate current
|
|
216
|
+
block building, and tip information into the one panel as well"). -->
|
|
217
|
+
<section class="page" data-page="space">
|
|
218
|
+
<div class="card w12" id="spaceCard">
|
|
219
|
+
<h3>Block space <span class="sp"></span><span class="src">the next block's worth of the mempool, richest first · area = vbytes, colour = feerate paid</span></h3>
|
|
220
|
+
<div class="spacelayout">
|
|
221
|
+
<div class="treemapwrap space">
|
|
222
|
+
<canvas class="treemap" id="spTreemap"></canvas><div class="viewer-ctl"><div class="refresh-in" data-refresh></div><button type="button" class="refresh-now" data-refresh-now disabled>refresh now</button></div>
|
|
223
|
+
<div class="goggles-tip hidden"></div>
|
|
224
|
+
</div>
|
|
225
|
+
<aside class="spacehud">
|
|
226
|
+
<div class="hud" id="spNext"></div>
|
|
227
|
+
<div class="hud" id="spTip"></div>
|
|
228
|
+
<div class="hud" id="spLegend"></div>
|
|
229
|
+
</aside>
|
|
230
|
+
</div>
|
|
231
|
+
<div class="note tiny faint" id="spNote"></div>
|
|
232
|
+
</div>
|
|
233
|
+
</section>
|
|
234
|
+
|
|
235
|
+
<!-- ==================================================== CHAIN & SYNC -->
|
|
236
|
+
<section class="page" data-page="chain">
|
|
237
|
+
<div class="grid chgrid">
|
|
238
|
+
<div data-sync-hero="chain"></div>
|
|
239
|
+
<!-- THREE ACROSS, SHORT (operator, 2026-09-11: "surely we can fit more info on here").
|
|
240
|
+
Two columns of tall charts made the page 1,518 px at 1483 x 1000 and 1,762 at
|
|
241
|
+
1280 x 860; the tip chart is a flat line once synced, and Chain state was a 12-row
|
|
242
|
+
single column. Charts are three to a row at 110 px (tip progression last), the
|
|
243
|
+
state and UTXO/difficulty cards three across with two-column lists. -->
|
|
244
|
+
|
|
245
|
+
<div class="card w4">
|
|
246
|
+
<h3>Block interval <span class="sp"></span><span class="src">inter-arrival, last 24h</span></h3>
|
|
247
|
+
<canvas class="chart" id="chGapChart"></canvas>
|
|
248
|
+
<div class="note tiny faint" id="chGapNote"></div>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
<div class="card w4">
|
|
252
|
+
<h3>Block size <span class="sp"></span><span class="src">bytes, sum of transaction sizes</span></h3>
|
|
253
|
+
<canvas class="chart" id="chSizeChart"></canvas>
|
|
254
|
+
<div class="note tiny faint" id="chSizeNote"></div>
|
|
255
|
+
</div>
|
|
256
|
+
|
|
257
|
+
<div class="card w4">
|
|
258
|
+
<h3>Fees per block <span class="sp"></span><span class="src">satsoshis</span></h3>
|
|
259
|
+
<canvas class="chart" id="chFeeChart"></canvas>
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
<div class="card w4">
|
|
263
|
+
<h3>Transactions per block <span class="sp"></span><span class="src">count</span></h3>
|
|
264
|
+
<canvas class="chart" id="chTxChart"></canvas>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<div class="card w4">
|
|
268
|
+
<h3>Transaction rate <span class="sp"></span><span class="src">getchaintxstats txrate</span></h3>
|
|
269
|
+
<canvas class="chart" id="chTxRateChart"></canvas>
|
|
270
|
+
<div class="mksum mt-6 chtx" id="chTxStats"></div>
|
|
271
|
+
</div>
|
|
272
|
+
|
|
273
|
+
<div class="card w4">
|
|
274
|
+
<h3>Tip progression <span class="sp"></span><span class="src">blocks vs headers</span></h3>
|
|
275
|
+
<canvas class="chart" id="chTipChart"></canvas>
|
|
276
|
+
<div class="note tiny faint" id="chTipNote"></div>
|
|
277
|
+
</div>
|
|
278
|
+
|
|
279
|
+
<div class="card w4">
|
|
280
|
+
<h3>Chain state <span class="sp"></span><span class="src">getblockchaininfo</span></h3>
|
|
281
|
+
<dl class="kv two" id="chState"></dl>
|
|
282
|
+
</div>
|
|
283
|
+
|
|
284
|
+
<div class="card w4">
|
|
285
|
+
<h3>UTXO set <span class="sp"></span><span class="src">gettxoutsetinfo</span></h3>
|
|
286
|
+
<dl class="kv" id="chUtxo"></dl>
|
|
287
|
+
<canvas class="chart short" id="chUtxoChart"></canvas>
|
|
288
|
+
</div>
|
|
289
|
+
|
|
290
|
+
<div class="card w4">
|
|
291
|
+
<h3>Difficulty & work <span class="sp"></span><span class="src">difficulty</span></h3>
|
|
292
|
+
<dl class="kv" id="chDiff"></dl>
|
|
293
|
+
<canvas class="chart short" id="chDiffChart"></canvas>
|
|
294
|
+
</div>
|
|
295
|
+
|
|
296
|
+
<div class="card w6">
|
|
297
|
+
<h3>Block drill-down <span class="sp"></span><span class="src">getblock verbosity=1 + getblockstats</span></h3>
|
|
298
|
+
<div class="rowform">
|
|
299
|
+
<input class="f grow minw-180" id="bdQuery" placeholder="height or block hash (blank = current tip)" autocomplete="off">
|
|
300
|
+
<button class="btn primary" id="bdRun">inspect</button>
|
|
301
|
+
</div>
|
|
302
|
+
<div class="note tiny faint" id="bdNote">Header, statistics and the first page of txids. Not the block's transactions: this node returns 11 MB of hex for one verbose block, and its verbose form still omits Core's fee fields.</div>
|
|
303
|
+
<dl class="kv" id="bdHeader"></dl>
|
|
304
|
+
<div class="txid-list" id="bdTxids"></div>
|
|
305
|
+
<div class="drill" id="bdTx"></div>
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
<div class="card w6">
|
|
309
|
+
<h3>Indexes <span class="sp"></span><span class="src">getindexinfo</span></h3>
|
|
310
|
+
<table class="t" id="chIndexes"><thead><tr><th>index</th><th class="r">height</th><th>synced</th></tr></thead><tbody></tbody></table>
|
|
311
|
+
<div class="hr"></div>
|
|
312
|
+
<h3>Chain tips <span class="sp"></span><span class="src">getchaintips</span></h3>
|
|
313
|
+
<table class="t" id="chTips"><thead><tr><th>height</th><th class="r">branchlen</th><th>status</th></tr></thead><tbody></tbody></table>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
</section>
|
|
317
|
+
|
|
318
|
+
<!-- ========================================================== ABOUT -->
|
|
319
|
+
<!-- Reached from the monogram, not from a nav tab. The galaxy is the point here, not a
|
|
320
|
+
backdrop: this is the one page with no data for it to obscure. -->
|
|
321
|
+
<section class="page" data-page="about">
|
|
322
|
+
<div class="aboutwrap">
|
|
323
|
+
<canvas class="absky" id="abSky"></canvas>
|
|
324
|
+
<div class="aboutinner">
|
|
325
|
+
<div class="abhead">
|
|
326
|
+
<svg class="ablogo" viewBox="0 0 32 32" role="img" aria-label="BlockYard">
|
|
327
|
+
<defs><linearGradient id="abTile" x1="0" y1="0" x2="1" y2="1">
|
|
328
|
+
<stop offset="0" stop-color="#ffb85c"/><stop offset=".55" stop-color="#f7931a"/><stop offset="1" stop-color="#a8640d"/>
|
|
329
|
+
</linearGradient></defs>
|
|
330
|
+
<rect x=".5" y=".5" width="31" height="31" rx="7.5" fill="url(#abTile)"/>
|
|
331
|
+
<g fill="#16110a" opacity=".14"><rect x="4" y="24.6" width="8" height="3.6" rx="1"/><rect x="13" y="24.6" width="6" height="3.6" rx="1"/><rect x="20" y="24.6" width="8" height="3.6" rx="1"/></g>
|
|
332
|
+
<path fill="#1a1206" fill-rule="evenodd" d="M5 8h6.6a4.2 4.2 0 0 1 2.35 7.66A4.4 4.4 0 0 1 12 24H5Zm3.3 2.9v3.5h3.1a1.75 1.75 0 0 0 0-3.5Zm0 6.4v3.8h3.4a1.9 1.9 0 0 0 0-3.8Z"/>
|
|
333
|
+
<path fill="#1a1206" d="M16.8 8h3.3l2.75 5.3L25.6 8h3.3l-4.85 8.8V24h-3.3v-7.2Z"/>
|
|
334
|
+
</svg>
|
|
335
|
+
<div>
|
|
336
|
+
<h1>Block<b>Yard</b></h1>
|
|
337
|
+
<p>A self-hosted monitor, block explorer, markets view and 3D block-space viewer for a Bitcoin node. No dependencies, no telemetry, read-only toward your node.</p>
|
|
338
|
+
</div>
|
|
339
|
+
</div>
|
|
340
|
+
|
|
341
|
+
<div class="abcols">
|
|
342
|
+
<section class="abcard"><h3>This monitor</h3><div id="abApp"></div></section>
|
|
343
|
+
<section class="abcard"><h3>Host</h3><div id="abHost"></div></section>
|
|
344
|
+
<section class="abcard"><h3>Bitcoin node</h3><div id="abNode"></div></section>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
<div class="ablinks">
|
|
348
|
+
<a class="abgh" href="https://github.com/BobClawblaw/blockyard" target="_blank" rel="noopener noreferrer">
|
|
349
|
+
<!-- The GitHub mark, GitHub's trademark, used to link to the project on GitHub. -->
|
|
350
|
+
<svg viewBox="0 0 16 16" width="20" height="20" aria-hidden="true" focusable="false" fill="currentColor"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg>
|
|
351
|
+
<span>github.com/BobClawblaw/blockyard</span>
|
|
352
|
+
</a>
|
|
353
|
+
|
|
354
|
+
<!-- The donation address: one click copies it (operator, 2026-09-14). A button, not a link:
|
|
355
|
+
there is nowhere to go, and the address is the whole point. -->
|
|
356
|
+
<button type="button" class="abdon" id="abDonate" data-copy="bc1q249cv27lc2q7y0x53vkczgfvvgsjzhwxwv42gc" title="click to copy the address">
|
|
357
|
+
<!-- The QR is the address itself, generated once (scripts/donate-qr.py) and checked to decode back to it.
|
|
358
|
+
A PNG, not an inline SVG: Safari on the first Mac install painted the SVG's white ground and
|
|
359
|
+
not one of its 800 module rects (2026-09-15), and a bitmap is painted the same everywhere. -->
|
|
360
|
+
<img class="abqr" src="/donate-qr.png" width="132" height="132" alt="QR code of the donation address" decoding="async">
|
|
361
|
+
<span class="abdontext">
|
|
362
|
+
<span class="abdonlbl"><svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="11" fill="var(--accent)"/><path fill="#1a1206" d="M9.2 6.2h1.5V4.8h1.6v1.4h1.1V4.8H15v1.5c1.6.3 2.6 1.2 2.6 2.7 0 1-.5 1.8-1.3 2.2 1.2.4 1.9 1.3 1.9 2.6 0 1.8-1.3 2.8-3.2 3v1.5h-1.6v-1.4h-1.1v1.4h-1.6v-1.5H9.2v-1.8h1.1V8h-1.1zm2.6 3.9h1.7c1 0 1.6-.4 1.6-1.2 0-.8-.6-1.2-1.6-1.2h-1.7zm0 4.4h2c1.1 0 1.8-.5 1.8-1.3s-.7-1.3-1.8-1.3h-2z"/></svg> If BlockYard is useful, tips are welcome</span>
|
|
363
|
+
<code>bc1q249cv27lc2q7y0x53vkczgfvvgsjzhwxwv42gc</code>
|
|
364
|
+
<span class="abdonhint">scan, or click to copy</span>
|
|
365
|
+
</span>
|
|
366
|
+
</button>
|
|
367
|
+
</div>
|
|
368
|
+
|
|
369
|
+
<p class="abcredit">Entirely made by machines. Directed by human hands.</p>
|
|
370
|
+
</div>
|
|
371
|
+
</div>
|
|
372
|
+
</section>
|
|
373
|
+
|
|
374
|
+
<!-- ======================================================== MEMPOOL -->
|
|
375
|
+
<section class="page" data-page="mempool">
|
|
376
|
+
<div class="grid">
|
|
377
|
+
<!-- Two independent stacks, like the Overview (operator, 2026-09-11: "Too
|
|
378
|
+
much wasted space on this panel. We should compress, and add the
|
|
379
|
+
mempool display here too in a smaller panel"). The Pool usage card is
|
|
380
|
+
compact -- gauge beside its figures -- and the Block space viewer (the
|
|
381
|
+
same one as Overview, Mining and Block space) sits in the narrower
|
|
382
|
+
right column. -->
|
|
383
|
+
<div class="mpcols">
|
|
384
|
+
<!-- TWO COLUMNS, THE RIGHT ONE WIDER (operator, 2026-09-12: "Why can't we put Age
|
|
385
|
+
distribution next to Fee rate vs age under Fee estimates over time"). No reason at
|
|
386
|
+
all, and it balances the page: with three equal columns the figures-and-histogram
|
|
387
|
+
column ran 659px at a 1600px window while the other two sat at 375 and 392 -- a 284px
|
|
388
|
+
spread. The two distributions share a row under the time series instead.
|
|
389
|
+
These are flex columns, not a grid of cards: a grid stretches every card to its row's
|
|
390
|
+
tallest, which is what once held Mempool size open with ~130px of nothing inside it.
|
|
391
|
+
Here each card is exactly as tall as its contents. -->
|
|
392
|
+
<div class="ovcol">
|
|
393
|
+
<div class="card w3" id="mpUsageCard">
|
|
394
|
+
<h3>Pool usage <span class="sp"></span><span class="src">usage / maxmempool</span></h3>
|
|
395
|
+
<div class="mpusage">
|
|
396
|
+
<canvas class="chart meter" id="mpMeter"></canvas>
|
|
397
|
+
<dl class="kv" id="mpLimits"></dl>
|
|
398
|
+
</div>
|
|
399
|
+
</div>
|
|
400
|
+
<div class="card w6">
|
|
401
|
+
<h3>Fee rate distribution <span class="sp"></span><span class="src">sat/vB from getrawmempool (RPC)</span></h3>
|
|
402
|
+
<canvas class="chart" id="mpHistChart"></canvas>
|
|
403
|
+
<div class="note tiny faint" id="mpHistNote"></div>
|
|
404
|
+
</div>
|
|
405
|
+
</div>
|
|
406
|
+
<!-- No Block space viewer on this page (operator: "just remove the block space panel from
|
|
407
|
+
mempool entirely"). It lives on Overview, Block space and Mining; here it was the
|
|
408
|
+
tallest thing by a distance -- square, so its width was its height -- and it spent
|
|
409
|
+
most of this page's layout budget. -->
|
|
410
|
+
<div class="ovcol">
|
|
411
|
+
<div class="card w5">
|
|
412
|
+
<h3>Mempool size <span class="sp"></span><span class="src">count and bytes</span></h3>
|
|
413
|
+
<canvas class="chart" id="mpSizeChart"></canvas>
|
|
414
|
+
</div>
|
|
415
|
+
<div class="card w4">
|
|
416
|
+
<h3>Fee estimates over time <span class="sp"></span><span class="src">sat/vB</span></h3>
|
|
417
|
+
<canvas class="chart" id="mpFeeChart"></canvas>
|
|
418
|
+
</div>
|
|
419
|
+
<!-- the two distributions, side by side, under the time series -->
|
|
420
|
+
<div class="mprow">
|
|
421
|
+
<div class="card w6">
|
|
422
|
+
<h3>Fee rate vs age <span class="sp"></span><span class="src">sampled scatter</span></h3>
|
|
423
|
+
<canvas class="chart" id="mpScatter"></canvas>
|
|
424
|
+
<div class="note tiny faint" id="mpScatterNote"></div>
|
|
425
|
+
</div>
|
|
426
|
+
<div class="card w4">
|
|
427
|
+
<h3>Age distribution <span class="sp"></span><span class="src">seconds in pool</span></h3>
|
|
428
|
+
<canvas class="chart" id="mpAgeChart"></canvas>
|
|
429
|
+
</div>
|
|
430
|
+
</div>
|
|
431
|
+
</div>
|
|
432
|
+
</div>
|
|
433
|
+
|
|
434
|
+
<!-- WHAT THIS NODE CANNOT TELL US, as a strip rather than as rows. All three are absences:
|
|
435
|
+
two need the node's log (off on a monitor running RPC-only) and the third lists fields
|
|
436
|
+
getrawmempool omits. They are worth stating and not worth a third of the page. -->
|
|
437
|
+
<div class="card w4" id="mpAcceptCard">
|
|
438
|
+
<h3>Ingest & rejects <span class="sp"></span><span class="src">from the node's log</span></h3>
|
|
439
|
+
<dl class="kv" id="mpAccept"></dl>
|
|
440
|
+
<canvas class="chart short" id="mpIngestChart"></canvas>
|
|
441
|
+
</div>
|
|
442
|
+
|
|
443
|
+
<div class="card w4" id="mpOrphansCard">
|
|
444
|
+
<h3>Orphan pool <span class="sp"></span><span class="src">held, parked and resolved</span></h3>
|
|
445
|
+
<dl class="kv" id="mpOrphans"></dl>
|
|
446
|
+
<canvas class="chart short" id="mpOrphanChart"></canvas>
|
|
447
|
+
</div>
|
|
448
|
+
<div class="card w4">
|
|
449
|
+
<h3>Fields this node does not report <span class="sp"></span><span class="src">getrawmempool shape</span></h3>
|
|
450
|
+
<div class="note" id="mpNotReported"></div>
|
|
451
|
+
</div>
|
|
452
|
+
</div>
|
|
453
|
+
</section>
|
|
454
|
+
|
|
455
|
+
<!-- ========================================================== PEERS -->
|
|
456
|
+
<!-- ========================================================== KIOSK -->
|
|
457
|
+
<!-- The 3D Markets board and the Block space board side by side (public/js/kiosk.js). -->
|
|
458
|
+
<section class="page" data-page="kiosk">
|
|
459
|
+
<div class="kiosk" id="kiosk">
|
|
460
|
+
<div class="kcol">
|
|
461
|
+
<div class="kpanel kmk">
|
|
462
|
+
<div class="khead"><b>Markets</b><span class="faint" id="kMkLabel"></span></div>
|
|
463
|
+
<div class="treemapwrap kboard"><canvas class="treemap" id="kMarkets"></canvas><div class="goggles-tip hidden"></div></div>
|
|
464
|
+
</div>
|
|
465
|
+
<!-- the price text in a panel of its own (operator: "Create a new panel for the price text to
|
|
466
|
+
exist in, and keep the markets in it's own panel") -->
|
|
467
|
+
<!-- PRICE AND THE BOOK, in one panel of its own (operator, 2026-09-12: "Make it a new
|
|
468
|
+
panel with price, obviously"). The chart needs real height, which it cannot get as a
|
|
469
|
+
strip under the markets board, so the panel is sized like the Block flow one. -->
|
|
470
|
+
<div class="kpanel kpp">
|
|
471
|
+
<div class="khead"><b>Price & order book depth</b><span class="faint">five exchanges' tickers, and their books</span></div>
|
|
472
|
+
<div class="kprice" id="kPrice"></div>
|
|
473
|
+
<!-- The depth chart is a SIBLING of #kPrice, not a child: renderPriceInfo replaces that
|
|
474
|
+
element's innerHTML on every refresh, which would destroy a canvas inside it. -->
|
|
475
|
+
<div class="kdepthwrap"><canvas class="kdepth" id="kDepth"></canvas></div>
|
|
476
|
+
</div>
|
|
477
|
+
</div>
|
|
478
|
+
<div class="kcol">
|
|
479
|
+
<div class="kpanel ksp">
|
|
480
|
+
<div class="khead"><b>Block space</b><span class="faint">the next block's worth of the mempool</span></div>
|
|
481
|
+
<div class="treemapwrap kboard"><canvas class="treemap" id="kSpace"></canvas><div class="viewer-ctl"><div class="refresh-in" data-refresh></div><button type="button" class="refresh-now" data-refresh-now disabled>refresh now</button></div><div class="goggles-tip hidden"></div></div>
|
|
482
|
+
</div>
|
|
483
|
+
<div class="kpanel kbf flowtight">
|
|
484
|
+
<!-- the full-screen button lives in this head now (2026-09-15): floated at the kiosk's
|
|
485
|
+
corner it sat over the flow's cards once the strip was tightened -->
|
|
486
|
+
<div class="khead"><b>Block flow</b><span class="faint">projected → being built → the chain</span><span class="sp"></span><button type="button" class="kfull" id="kFull" title="full screen">full screen</button></div>
|
|
487
|
+
<div id="kTrain" class="flowwrap"></div>
|
|
488
|
+
</div>
|
|
489
|
+
</div>
|
|
490
|
+
</div>
|
|
491
|
+
</section>
|
|
492
|
+
<!-- TETRUST: a Tetris on the 3D engine (operator, 2026-09-12). The well is a board3d, so it wears
|
|
493
|
+
every display setting the Block space board does: stars, the galaxy, neon edges, the sheen. -->
|
|
494
|
+
<!-- BLOCKOUT (public/js/blockout.js, rules in breakout.js): Breakout on the block engine. The
|
|
495
|
+
court is a board3d, the bricks are one grid cell each, and the panel behind it is the same
|
|
496
|
+
sky the Block space board draws. -->
|
|
497
|
+
<section class="page" data-page="blockout">
|
|
498
|
+
<div class="tetrust" id="blockout">
|
|
499
|
+
<div class="card tetcard bocard">
|
|
500
|
+
<canvas class="tetsky" id="boSky"></canvas>
|
|
501
|
+
<h3>Blockout <span class="sp"></span><span class="src">break the wall — the bat follows your mouse</span></h3>
|
|
502
|
+
<div class="treemapwrap tetwell bowell idle" id="boWellWrap">
|
|
503
|
+
<canvas class="treemap" id="boWell"></canvas>
|
|
504
|
+
</div>
|
|
505
|
+
<div class="tetover" id="boOver">
|
|
506
|
+
<div class="tetmsg" id="boMsg">Blockout</div>
|
|
507
|
+
<div class="tetsub" id="boSub"></div>
|
|
508
|
+
<button type="button" class="btn primary" id="boResume">play</button>
|
|
509
|
+
</div>
|
|
510
|
+
</div>
|
|
511
|
+
<div class="card tethud">
|
|
512
|
+
<h3>Score</h3>
|
|
513
|
+
<div class="tetstats" id="boStats"></div>
|
|
514
|
+
<h3>Keys</h3>
|
|
515
|
+
<div class="tetkeys">move the mouse over the court, or ← → / A D · space or click serves · P pause · Enter play</div>
|
|
516
|
+
<h3>Switches</h3>
|
|
517
|
+
<div class="tetswitches">
|
|
518
|
+
<button type="button" class="tetsw" id="boStars" aria-pressed="false" title="the star field across the panel">★ stars</button>
|
|
519
|
+
<button type="button" class="tetsw" id="boGalaxy" aria-pressed="false" title="the spiral galaxy in that sky">🌀 galaxy</button>
|
|
520
|
+
<button type="button" class="tetsw" id="boNeon" aria-pressed="false" title="the wall under lit neon tubes">◉ neon</button>
|
|
521
|
+
<button type="button" class="tetsw" id="boSfx" aria-pressed="false" title="bat, bricks, walls and a lost ball">♫ sound</button>
|
|
522
|
+
</div>
|
|
523
|
+
<h3>High scores <span class="sp"></span><span class="src">this browser</span></h3>
|
|
524
|
+
<table class="tetscores" id="boScores"></table>
|
|
525
|
+
</div>
|
|
526
|
+
</div>
|
|
527
|
+
</section>
|
|
528
|
+
<!-- BLOCKANOID (public/js/blockanoid.js, rules in arkanoid.js): Arkanoid on the block engine.
|
|
529
|
+
A narrower, taller court than Blockout's; silver bricks survive a hit, gold survives every
|
|
530
|
+
hit, and a broken brick may drop a capsule. Vaus turns red while the laser is up. -->
|
|
531
|
+
<section class="page" data-page="blockanoid">
|
|
532
|
+
<div class="tetrust" id="blockanoid">
|
|
533
|
+
<div class="card tetcard bacard">
|
|
534
|
+
<canvas class="tetsky" id="baSky"></canvas>
|
|
535
|
+
<h3>Blockanoid <span class="sp"></span><span class="src">catch the capsules — Vaus follows your mouse</span></h3>
|
|
536
|
+
<div class="treemapwrap tetwell banwell idle" id="baWellWrap">
|
|
537
|
+
<canvas class="treemap" id="baWell"></canvas>
|
|
538
|
+
</div>
|
|
539
|
+
<div class="tetover" id="baOver">
|
|
540
|
+
<div class="tetmsg" id="baMsg">Blockanoid</div>
|
|
541
|
+
<div class="tetsub" id="baSub"></div>
|
|
542
|
+
<button type="button" class="btn primary" id="baResume">play</button>
|
|
543
|
+
</div>
|
|
544
|
+
</div>
|
|
545
|
+
<div class="card tethud">
|
|
546
|
+
<h3>Score</h3>
|
|
547
|
+
<div class="tetstats" id="baStats"></div>
|
|
548
|
+
<h3>Capsules</h3>
|
|
549
|
+
<!-- Each capsule has its own SHAPE on the court, not just its own colour, so the legend
|
|
550
|
+
names the shape too -- the swatch is a class, never a style attribute (CSP). -->
|
|
551
|
+
<ul class="caplegend">
|
|
552
|
+
<li><i class="capsw cap-laser">L</i><b>laser</b><span>twin cannons</span></li>
|
|
553
|
+
<li><i class="capsw cap-enlarge">E</i><b>wide</b><span>flat bar, capped ends</span></li>
|
|
554
|
+
<li><i class="capsw cap-catch">C</i><b>catch</b><span>ball in a cradle</span></li>
|
|
555
|
+
<li><i class="capsw cap-slow">S</i><b>slow</b><span>hollow frame</span></li>
|
|
556
|
+
<li><i class="capsw cap-disrupt">D</i><b>three balls</b><span>three beads</span></li>
|
|
557
|
+
<li><i class="capsw cap-player">P</i><b>a life</b><span>tall tower</span></li>
|
|
558
|
+
</ul>
|
|
559
|
+
<h3>Keys</h3>
|
|
560
|
+
<div class="tetkeys">move the mouse over the court, or ← → / A D · space or click serves · the laser fires itself while armed · P pause · Enter play</div>
|
|
561
|
+
<h3>Switches</h3>
|
|
562
|
+
<div class="tetswitches">
|
|
563
|
+
<button type="button" class="tetsw" id="baStars" aria-pressed="false" title="the star field across the panel">★ stars</button>
|
|
564
|
+
<button type="button" class="tetsw" id="baGalaxy" aria-pressed="false" title="the spiral galaxy in that sky">🌀 galaxy</button>
|
|
565
|
+
<button type="button" class="tetsw" id="baNeon" aria-pressed="false" title="the wall under lit neon tubes">◉ neon</button>
|
|
566
|
+
<button type="button" class="tetsw" id="baSfx" aria-pressed="false" title="Vaus, bricks, capsules, the laser and a lost ball">♫ sound</button>
|
|
567
|
+
</div>
|
|
568
|
+
<h3>High scores <span class="sp"></span><span class="src">this browser</span></h3>
|
|
569
|
+
<table class="tetscores" id="baScores"></table>
|
|
570
|
+
</div>
|
|
571
|
+
</div>
|
|
572
|
+
</section>
|
|
573
|
+
|
|
574
|
+
<section class="page" data-page="tetrust">
|
|
575
|
+
<div class="tetrust" id="tetrust">
|
|
576
|
+
<div class="card tetcard">
|
|
577
|
+
<canvas class="tetsky" id="tetSky"></canvas>
|
|
578
|
+
<h3>Tetrust <span class="sp"></span><span class="src">trust, but verify — every line you clear is a block you verified</span></h3>
|
|
579
|
+
<div class="treemapwrap tetwell idle" id="tetWellWrap">
|
|
580
|
+
<canvas class="treemap" id="tetWell"></canvas>
|
|
581
|
+
</div>
|
|
582
|
+
<div class="tetover" id="tetOver">
|
|
583
|
+
<div class="tetmsg" id="tetMsg">Tetrust</div>
|
|
584
|
+
<div class="tetsub" id="tetSub"></div>
|
|
585
|
+
<button type="button" class="btn primary" id="tetResume">play</button>
|
|
586
|
+
</div>
|
|
587
|
+
</div>
|
|
588
|
+
<div class="card tethud">
|
|
589
|
+
<h3>Score</h3>
|
|
590
|
+
<div class="tetstats" id="tetStats"></div>
|
|
591
|
+
<h3>Next</h3>
|
|
592
|
+
<div class="treemapwrap tetnext"><canvas class="treemap" id="tetNext"></canvas></div>
|
|
593
|
+
<h3>Keys</h3>
|
|
594
|
+
<div class="tetkeys">← → or A D move · ↑ W or X rotate · Z or Q rotate back · ↓ or S soft drop · space hard drop · P pause · Enter play</div>
|
|
595
|
+
<h3>Switches</h3>
|
|
596
|
+
<div class="tetswitches">
|
|
597
|
+
<button type="button" class="tetsw" id="tetStars" aria-pressed="false" title="the star field across the panel">★ stars</button>
|
|
598
|
+
<button type="button" class="tetsw" id="tetGalaxy" aria-pressed="false" title="the spiral galaxy in that sky">🌀 galaxy</button>
|
|
599
|
+
<button type="button" class="tetsw" id="tetMusic" aria-pressed="false" title="Korobeiniki, on oscillators">♪ music</button>
|
|
600
|
+
<button type="button" class="tetsw" id="tetSfx" aria-pressed="false" title="move, rotate, drop, clear, game over">♫ sound</button>
|
|
601
|
+
</div>
|
|
602
|
+
<h3>High scores <span class="sp"></span><span class="src">this browser</span></h3>
|
|
603
|
+
<table class="tetscores" id="tetScores"></table>
|
|
604
|
+
</div>
|
|
605
|
+
</div>
|
|
606
|
+
</section>
|
|
607
|
+
|
|
608
|
+
<!-- WOLFENSTEIN 3D (public/js/wolf3d.js): the shareware WOLF3D.EXE v1.4 from games/wolf3d_dos/,
|
|
609
|
+
unmodified, on the same emulated PC -- this time in real mode, as a 16-bit DOS program. -->
|
|
610
|
+
<section class="page" data-page="wolf3d">
|
|
611
|
+
<div class="tetrust dosgame" id="wolf3d">
|
|
612
|
+
<div class="card doscard">
|
|
613
|
+
<h3>Wolfenstein 3D <span class="sp"></span><span class="src">shareware v1.4 · Escape from Wolfenstein · on an emulated PC</span></h3>
|
|
614
|
+
<div class="doswrap" id="wolfWrap">
|
|
615
|
+
<canvas class="dosscreen" id="wolfScreen" width="320" height="200"></canvas>
|
|
616
|
+
<div class="tetover" id="wolfOver">
|
|
617
|
+
<div class="tetmsg" id="wolfMsg">Wolfenstein 3D</div>
|
|
618
|
+
<div class="tetsub" id="wolfSub"></div>
|
|
619
|
+
<button type="button" class="btn primary" id="wolfPlay">play</button>
|
|
620
|
+
</div>
|
|
621
|
+
<button type="button" class="btn primary dosagain hidden" id="wolfPlay2">play again</button>
|
|
622
|
+
</div>
|
|
623
|
+
</div>
|
|
624
|
+
<div class="card tethud">
|
|
625
|
+
<h3>Machine</h3>
|
|
626
|
+
<div class="tetstats" id="wolfStatus"></div>
|
|
627
|
+
<h3>Keys</h3>
|
|
628
|
+
<div class="tetkeys" id="wolfKeys"></div>
|
|
629
|
+
<div class="tetkeys">click the screen to capture the mouse (Esc gives it back): moving it turns, forward and back walk, left click fires</div>
|
|
630
|
+
<h3>Switches</h3>
|
|
631
|
+
<div class="tetswitches">
|
|
632
|
+
<button type="button" class="tetsw" id="wolfSound" aria-pressed="false" title="the Sound Blaster: AdLib music and digitised effects">♫ sound</button>
|
|
633
|
+
<button type="button" class="tetsw" id="wolfSmooth" aria-pressed="false" title="smooth the 320x200 picture instead of showing its pixels">◌ smooth</button>
|
|
634
|
+
<button type="button" class="tetsw" id="wolfFull" title="the screen alone, full screen">⛶ full screen</button>
|
|
635
|
+
</div>
|
|
636
|
+
<h3>About</h3>
|
|
637
|
+
<div class="tetkeys">Wolfenstein 3D is id Software's (1992, published by Apogee), from the shareware files in <code>games/wolf3d_dos/</code> on this server. The PC under it is BlockYard's own, here running in real mode: a 16-bit DOS program with a VGA and a Sound Blaster, written in JavaScript with no dependencies. Savegames and settings stay in this browser.</div>
|
|
638
|
+
</div>
|
|
639
|
+
</div>
|
|
640
|
+
</section>
|
|
641
|
+
|
|
642
|
+
<!-- DOOM (public/js/doom.js): the shareware DOOM.EXE v1.9 from games/doom_dos/, unmodified, on a PC this
|
|
643
|
+
repository emulates -- x86.js the CPU, dospc.js DOS/4GW and the hardware, soundcard.js a
|
|
644
|
+
Sound Blaster Pro 2 -- running in a worker (dosworker.js). The canvas is the monitor. -->
|
|
645
|
+
<section class="page" data-page="doom">
|
|
646
|
+
<div class="tetrust dosgame" id="doom">
|
|
647
|
+
<div class="card doscard">
|
|
648
|
+
<h3>DOOM <span class="sp"></span><span class="src">shareware v1.9 · Knee-Deep in the Dead · on an emulated 486</span></h3>
|
|
649
|
+
<div class="doswrap" id="doomWrap">
|
|
650
|
+
<canvas class="dosscreen" id="doomScreen" width="320" height="200"></canvas>
|
|
651
|
+
<div class="tetover" id="doomOver">
|
|
652
|
+
<div class="tetmsg" id="doomMsg">DOOM</div>
|
|
653
|
+
<div class="tetsub" id="doomSub"></div>
|
|
654
|
+
<button type="button" class="btn primary" id="doomPlay">play</button>
|
|
655
|
+
</div>
|
|
656
|
+
<button type="button" class="btn primary dosagain hidden" id="doomPlay2">play again</button>
|
|
657
|
+
</div>
|
|
658
|
+
</div>
|
|
659
|
+
<div class="card tethud">
|
|
660
|
+
<h3>Machine</h3>
|
|
661
|
+
<div class="tetstats" id="doomStatus"></div>
|
|
662
|
+
<h3>Keys</h3>
|
|
663
|
+
<div class="tetkeys" id="doomKeys"></div>
|
|
664
|
+
<div class="tetkeys">click the screen to capture the mouse (Esc gives it back) · left fire · right strafe · middle walk</div>
|
|
665
|
+
<h3>Switches</h3>
|
|
666
|
+
<div class="tetswitches">
|
|
667
|
+
<button type="button" class="tetsw" id="doomSound" aria-pressed="false" title="the Sound Blaster: effects and the OPL music">♫ sound</button>
|
|
668
|
+
<button type="button" class="tetsw" id="doomWasd" aria-pressed="false" title="W A S D to walk and strafe, E to open (on by default); off gives DOOM's own arrows, Space and Alt. Applies at once">⌨ WASD</button>
|
|
669
|
+
<button type="button" class="tetsw" id="doomSmooth" aria-pressed="false" title="smooth the 320x200 picture instead of showing its pixels">◌ smooth</button>
|
|
670
|
+
<button type="button" class="tetsw" id="doomFull" title="the screen alone, full screen">⛶ full screen</button>
|
|
671
|
+
</div>
|
|
672
|
+
<h3>About</h3>
|
|
673
|
+
<div class="tetkeys">DOOM is id Software's (1993), from the shareware files in <code>games/doom_dos/</code> on this server. The PC under it is BlockYard's own: an i386, DOS/4GW, a VGA and a Sound Blaster written in JavaScript, with no dependencies. Savegames and settings stay in this browser.</div>
|
|
674
|
+
</div>
|
|
675
|
+
</div>
|
|
676
|
+
</section>
|
|
677
|
+
|
|
678
|
+
<!-- QUAKE (public/js/quake.js): the shareware QUAKE.EXE v1.06 from games/quake_dos/, unmodified, on the
|
|
679
|
+
same emulated PC as DOOM -- this time playing the go32 stub and CWSDPMI for a DJGPP program. -->
|
|
680
|
+
<section class="page" data-page="quake">
|
|
681
|
+
<div class="tetrust dosgame" id="quake">
|
|
682
|
+
<div class="card doscard">
|
|
683
|
+
<h3>Quake <span class="sp"></span><span class="src">shareware v1.06 · Dimension of the Doomed · on an emulated PC</span></h3>
|
|
684
|
+
<div class="doswrap" id="quakeWrap">
|
|
685
|
+
<canvas class="dosscreen" id="quakeScreen" width="320" height="200"></canvas>
|
|
686
|
+
<div class="tetover" id="quakeOver">
|
|
687
|
+
<div class="tetmsg" id="quakeMsg">Quake</div>
|
|
688
|
+
<div class="tetsub" id="quakeSub"></div>
|
|
689
|
+
<button type="button" class="btn primary" id="quakePlay">play</button>
|
|
690
|
+
</div>
|
|
691
|
+
<button type="button" class="btn primary dosagain hidden" id="quakePlay2">play again</button>
|
|
692
|
+
</div>
|
|
693
|
+
</div>
|
|
694
|
+
<div class="card tethud">
|
|
695
|
+
<h3>Machine</h3>
|
|
696
|
+
<div class="tetstats" id="quakeStatus"></div>
|
|
697
|
+
<h3>Keys</h3>
|
|
698
|
+
<div class="tetkeys" id="quakeKeys"></div>
|
|
699
|
+
<div class="tetkeys">click the screen to capture the mouse (Esc gives it back) · W A S D and mouse look to start with; rebind anything in Quake's own options menu</div>
|
|
700
|
+
<h3>Switches</h3>
|
|
701
|
+
<div class="tetswitches">
|
|
702
|
+
<button type="button" class="tetsw" id="quakeSound" aria-pressed="false" title="the Sound Blaster: Quake's effects (its music was on the CD)">♫ sound</button>
|
|
703
|
+
<button type="button" class="tetsw" id="quakeSmooth" aria-pressed="false" title="smooth the 320x200 picture instead of showing its pixels">◌ smooth</button>
|
|
704
|
+
<button type="button" class="tetsw" id="quakeFull" title="the screen alone, full screen">⛶ full screen</button>
|
|
705
|
+
</div>
|
|
706
|
+
<h3>About</h3>
|
|
707
|
+
<div class="tetkeys">Quake is id Software's (1996), from the shareware files in <code>games/quake_dos/</code> on this server. The PC under it is BlockYard's own: an i386 with its FPU, a DPMI host, a VGA and a Sound Blaster written in JavaScript, with no dependencies. Savegames and settings stay in this browser.</div>
|
|
708
|
+
</div>
|
|
709
|
+
</div>
|
|
710
|
+
</section>
|
|
711
|
+
|
|
712
|
+
<!-- ======================================================== MARKETS -->
|
|
713
|
+
<!-- Exchange prices on the block-space board (public/js/markets.js, server/collect/markets.js). -->
|
|
714
|
+
<section class="page" data-page="markets">
|
|
715
|
+
<div class="grid">
|
|
716
|
+
<div class="card w12 mkcard">
|
|
717
|
+
<h3>Markets <span class="sp"></span><span class="src">public exchange APIs · hourly candles · fetched only while this tab is open</span></h3>
|
|
718
|
+
<div class="mkbar" id="mkBar"></div>
|
|
719
|
+
<!-- THE VISUALIZER FIRST (operator: "move the 3D View to the top of the markets display. That
|
|
720
|
+
should be our main selling point, is the spectacular visualizer") -->
|
|
721
|
+
<!-- ONE PRICE VIEW AT A TIME (operator, 2026-09-12: "have a selector for either the 3D
|
|
722
|
+
view or 2D view for price. Not both at the same time. Too much waste of space for
|
|
723
|
+
that screen"). Both elements stay in the page -- the toolbar switches between them
|
|
724
|
+
live, and markets.js skips the DRAW for whichever is hidden, so the unseen one costs
|
|
725
|
+
nothing per refresh. The 3D board keeps its place first; a test pins that order. -->
|
|
726
|
+
<!-- THE FIGURES SIT UNDER THE TOOLBAR IN BOTH VIEWS (operator, 2026-09-12: of the median,
|
|
727
|
+
spread, volume and reporting line, "that line is also missing on the 3D view"). It was
|
|
728
|
+
never missing -- it sat AFTER the board in the markup, so with the flat chart showing
|
|
729
|
+
it landed 37px under the toolbar, and with the 600px board showing it was pushed 647px
|
|
730
|
+
down and stranded below the picture. Measured both ways. One line of figures belongs
|
|
731
|
+
with the price, whichever way the price is drawn. -->
|
|
732
|
+
<div class="mksum" id="mkSummary"></div>
|
|
733
|
+
<!-- No legend beside the board (operator, 2026-09-12: "get rid of this. takes up too much
|
|
734
|
+
space on the markets page"). It was a 250px column of prose explaining that a green
|
|
735
|
+
body closed up and a red one closed down, next to a chart that shows exactly that.
|
|
736
|
+
The board takes the full width now. -->
|
|
737
|
+
<div class="mklayout" id="mkLayout">
|
|
738
|
+
<div class="treemapwrap mkboard"><canvas class="treemap" id="mkBoard"></canvas><div class="goggles-tip hidden"></div></div>
|
|
739
|
+
</div>
|
|
740
|
+
<canvas class="mkchart" id="mkChart"></canvas>
|
|
741
|
+
<!-- THE TABLE AND THE BOOK SIDE BY SIDE (2026-09-15, "same treatment on the markets tab"):
|
|
742
|
+
stacked, the depth chart began 1,000px down and was never on the first screen; the
|
|
743
|
+
board now takes what the window leaves after this row, and the row is two columns
|
|
744
|
+
from 900px up. -->
|
|
745
|
+
<div class="mkbottom">
|
|
746
|
+
<div class="mkbottom-l">
|
|
747
|
+
<div id="mkTable"></div>
|
|
748
|
+
<div class="note tiny faint" id="mkNote"></div>
|
|
749
|
+
</div>
|
|
750
|
+
<div class="mkbottom-r">
|
|
751
|
+
<h3 class="mksub">Order book depth <span class="faint">cumulative bids and asks against price · each exchange's book, their total, and the total as it stood · change bars on the right, log scale</span></h3>
|
|
752
|
+
<div class="mkbar" id="mkDepthBar"></div>
|
|
753
|
+
<canvas class="mkchart mkdepth" id="mkDepth"></canvas>
|
|
754
|
+
<div class="note tiny faint" id="mkDepthNote"></div>
|
|
755
|
+
</div>
|
|
756
|
+
</div>
|
|
757
|
+
</div>
|
|
758
|
+
</div>
|
|
759
|
+
</section>
|
|
760
|
+
|
|
761
|
+
<!-- ======================================================= EXPLORER -->
|
|
762
|
+
<!-- Blocks, transactions and addresses from this node (public/js/explorer.js). The route is
|
|
763
|
+
on the URL: #explorer/block/<height>, #explorer/tx/<txid>, #explorer/address/<addr>. -->
|
|
764
|
+
<section class="page" data-page="explorer">
|
|
765
|
+
<div class="grid">
|
|
766
|
+
<div class="card w12 xcard">
|
|
767
|
+
<form id="xSearch" class="xsearch" role="search" autocomplete="off">
|
|
768
|
+
<input id="xQuery" type="search" spellcheck="false" placeholder="block height, block hash, transaction id or address" aria-label="search the chain">
|
|
769
|
+
<button type="submit">Search</button>
|
|
770
|
+
</form>
|
|
771
|
+
<div id="xBody"></div>
|
|
772
|
+
</div>
|
|
773
|
+
</div>
|
|
774
|
+
</section>
|
|
775
|
+
|
|
776
|
+
<section class="page" data-page="peers">
|
|
777
|
+
<div class="grid">
|
|
778
|
+
<!-- THE PEER TABLE IS THE PAGE (operator, 2026-09-11: "We need to really compress the
|
|
779
|
+
panels in this screen to show more data. I don't want to have to scroll down to see
|
|
780
|
+
the primary information for that tab"). Two Connections cards showed the same count,
|
|
781
|
+
two charts the same 24 hours, and a seven-row budget list answered "–" or "not
|
|
782
|
+
reported" to every row on this build, all before the first peer. Now one strip --
|
|
783
|
+
the count with what the node does report, then one short history -- and the table. -->
|
|
784
|
+
<div class="prtop">
|
|
785
|
+
<div class="card prsum">
|
|
786
|
+
<h3>Connections <span class="sp"></span><span class="src">getconnectioncount · getnetworkinfo</span></h3>
|
|
787
|
+
<div class="metric"><div><span class="v" id="prCount">–</span></div><div class="s" id="prSplit">–</div></div>
|
|
788
|
+
<dl class="kv prbudget" id="prBudget"></dl>
|
|
789
|
+
</div>
|
|
790
|
+
<div class="card">
|
|
791
|
+
<h3>Connection history <span class="sp"></span><span class="src">in / out over 24h</span></h3>
|
|
792
|
+
<canvas class="chart short" id="prChart"></canvas>
|
|
793
|
+
</div>
|
|
794
|
+
</div>
|
|
795
|
+
<div class="card w12 prpeers">
|
|
796
|
+
<h3>Peers <span class="sp"></span><span class="src">getpeerinfo, every 15 s · bytes and rates per connection</span></h3>
|
|
797
|
+
<div class="note tiny faint" id="prIdentityNote"></div>
|
|
798
|
+
<div id="prTable"></div>
|
|
799
|
+
</div>
|
|
800
|
+
</div>
|
|
801
|
+
</section>
|
|
802
|
+
|
|
803
|
+
<!-- ======================================================== NETWORK -->
|
|
804
|
+
<section class="page" data-page="network">
|
|
805
|
+
<div class="grid">
|
|
806
|
+
<!-- TWO STACKS (2026-09-15, "same treatment on the network tab"): Throughput on the left,
|
|
807
|
+
Upload over Traffic accounting on the right, so the accounting card no longer sits
|
|
808
|
+
alone on a row beside half a screen of nothing; the sources table takes the rest. -->
|
|
809
|
+
<div class="ovcols ntcols">
|
|
810
|
+
<div class="ovcol">
|
|
811
|
+
<div class="card">
|
|
812
|
+
<h3>Throughput <span class="sp"></span><span class="src">getnettotals (RPC)</span></h3>
|
|
813
|
+
<div class="metric"><div class="l">network in</div><div><span class="v" id="ovNetIn">–</span><span class="u" id="ovNetInU"></span></div>
|
|
814
|
+
<div class="s" id="ovDisk">–</div></div>
|
|
815
|
+
<canvas class="chart tall" id="ovNetChart"></canvas>
|
|
816
|
+
</div>
|
|
817
|
+
</div>
|
|
818
|
+
<div class="ovcol">
|
|
819
|
+
<div class="card">
|
|
820
|
+
<h3>Upload <span class="sp"></span><span class="src">what the node reports</span></h3>
|
|
821
|
+
<div class="note" id="ntUploadNote"></div>
|
|
822
|
+
<dl class="kv" id="ntRpc"></dl>
|
|
823
|
+
</div>
|
|
824
|
+
<div class="card">
|
|
825
|
+
<h3>Traffic accounting <span class="sp"></span><span class="src">cumulative</span></h3>
|
|
826
|
+
<dl class="kv" id="ntAccounting"></dl>
|
|
827
|
+
</div>
|
|
828
|
+
</div>
|
|
829
|
+
</div>
|
|
830
|
+
<div class="card w12">
|
|
831
|
+
<h3>Where each number comes from <span class="sp"></span><span class="src">explicit sources</span></h3>
|
|
832
|
+
<div class="note" id="ntSources"></div>
|
|
833
|
+
</div>
|
|
834
|
+
</div>
|
|
835
|
+
</section>
|
|
836
|
+
|
|
837
|
+
<!-- ========================================================= EVENTS -->
|
|
838
|
+
<section class="page" data-page="mining">
|
|
839
|
+
<div class="grid">
|
|
840
|
+
<!-- THE NETWORK OVER A WEEK AND A YEAR (operator, 2026-09-15, with mempool.space's mining
|
|
841
|
+
dashboard: "Why don't we have this view in our mining tab?" -- "Do it"; then its
|
|
842
|
+
adjustments table: "add this too"). Every figure is from this node: getblockstats for
|
|
843
|
+
the reward window, block headers for the periods and the daily hashrate samples, the
|
|
844
|
+
coinbases of the last week's blocks for the pools (collect/network.js). -->
|
|
845
|
+
<!-- ONE SCREEN (operator, 2026-09-15: "remove the mempool space viewer from the mining tab.
|
|
846
|
+
Move Block flow up as the first thing along the top. Shuffle around and try to fit as
|
|
847
|
+
much as possible on one screen"): the flow across the top, then three stacks -- the
|
|
848
|
+
figures and the packages; the pool pie and the landscape; the year chart, the
|
|
849
|
+
adjustments, the window's pools and the attribution. The Mempool space viewer is on
|
|
850
|
+
Overview, Block space and Mempool already. -->
|
|
851
|
+
<!-- THE DEFAULT LAYOUT OF mempool.space's MINING DASHBOARD (operator, 2026-09-15: "look at the
|
|
852
|
+
source code for mempool space app, and try to implement this default layout at the very
|
|
853
|
+
least. Notice the 'View more >>' that open up a panel to see a full screen view of just
|
|
854
|
+
that panel"). Its mining-dashboard.component.html is two columns: reward stats |
|
|
855
|
+
difficulty adjustment; pool ranking | hashrate chart, each with "View more »"; recent
|
|
856
|
+
blocks | adjustments. The same six cards here in the same order, the block flow across
|
|
857
|
+
the top as before, and "View more »" opens the card full-screen (#expandWrap). Every
|
|
858
|
+
figure is from this node (collect/network.js); our own panels follow beneath. -->
|
|
859
|
+
<div class="card mnflow flowtight">
|
|
860
|
+
<h3>Block flow <span class="sp"></span><span class="src">being assembled → <b>the current block</b> → history receding</span></h3>
|
|
861
|
+
<div id="mnFlow" class="flowwrap"></div>
|
|
862
|
+
</div>
|
|
863
|
+
<div class="mn2">
|
|
864
|
+
<div class="card">
|
|
865
|
+
<h3>Reward stats <span class="sp"></span><span class="src" id="mnRewardsSrc">last 144 blocks · getblockstats</span></h3>
|
|
866
|
+
<div class="netstats" id="mnRewards"></div>
|
|
867
|
+
</div>
|
|
868
|
+
<div class="card">
|
|
869
|
+
<h3>Difficulty adjustment <span class="sp"></span><span class="src">this period, from its first block's time</span></h3>
|
|
870
|
+
<div class="netstats" id="mnAdjust"></div>
|
|
871
|
+
</div>
|
|
872
|
+
<div class="card">
|
|
873
|
+
<h3>Pools <span class="sp"></span><span class="src" id="mnPoolsWeekSrc">the last week's blocks, by coinbase</span></h3>
|
|
874
|
+
<div class="netstats" id="mnPoolsWeek"></div>
|
|
875
|
+
<canvas class="chart donut" id="mnPoolDonut"></canvas>
|
|
876
|
+
<div class="note tiny faint" id="mnPoolsWeekNote"></div>
|
|
877
|
+
<a class="viewmore" href="#mining" data-expand="pools">View more »</a>
|
|
878
|
+
</div>
|
|
879
|
+
<div class="card">
|
|
880
|
+
<h3>Hashrate & difficulty <span class="sp"></span><span class="src">a year, one sample a day · getnetworkhashps over the week</span></h3>
|
|
881
|
+
<div class="netstats" id="mnHashrate"></div>
|
|
882
|
+
<canvas class="chart tall" id="mnHashChart"></canvas>
|
|
883
|
+
<div class="note tiny faint">Difficulty × 2³² over each day's mean block interval, from the headers — an estimate, as every hashrate figure is; the steps are the difficulty.</div>
|
|
884
|
+
<a class="viewmore" href="#mining" data-expand="hashrate">View more »</a>
|
|
885
|
+
</div>
|
|
886
|
+
<div class="card">
|
|
887
|
+
<h3>Recent blocks <span class="sp"></span><span class="src">height, pool, reward, fees · the attributed window</span></h3>
|
|
888
|
+
<div id="mnRecent"></div>
|
|
889
|
+
<a class="viewmore" href="#mining" data-expand="blocks">View more »</a>
|
|
890
|
+
</div>
|
|
891
|
+
<div class="card">
|
|
892
|
+
<h3>Adjustments <span class="sp"></span><span class="src">each period's first block against the one before</span></h3>
|
|
893
|
+
<div id="mnAdjustments"></div>
|
|
894
|
+
<a class="viewmore" href="#mining" data-expand="adjustments">View more »</a>
|
|
895
|
+
</div>
|
|
896
|
+
</div>
|
|
897
|
+
<!-- our own: the block being built and the attribution behind the pools -->
|
|
898
|
+
<div class="mnextra">
|
|
899
|
+
<div class="card">
|
|
900
|
+
<h3>Packages in the block being built <span class="sp"></span><span class="src">ancestor graph from getrawmempool · depends</span></h3>
|
|
901
|
+
<div id="mnPackages"></div>
|
|
902
|
+
</div>
|
|
903
|
+
<div class="card">
|
|
904
|
+
<h3>Feerate landscape <span class="sp"></span><span class="src">the transactions selected for the next block</span></h3>
|
|
905
|
+
<canvas class="chart" id="mnFeeLandscape"></canvas>
|
|
906
|
+
<div class="note tiny faint">Buckets are sat/vB. Green means the bucket pays well above the block's own median, red below — the same shape a miner is looking at when it decides what to include.</div>
|
|
907
|
+
</div>
|
|
908
|
+
<div class="card">
|
|
909
|
+
<h3>Pools in this window <span class="sp"></span><span class="src">coinbase tags, grouped by curated label</span></h3>
|
|
910
|
+
<div id="mnPools"></div>
|
|
911
|
+
</div>
|
|
912
|
+
<div class="card">
|
|
913
|
+
<h3>Attribution <span class="sp"></span><span class="src">what we read and from where</span></h3>
|
|
914
|
+
<div id="mnCoverage"></div>
|
|
915
|
+
</div>
|
|
916
|
+
<details class="card mndetailed">
|
|
917
|
+
<summary><h3>Detailed <span class="sp"></span><span class="src">what this node can and cannot answer</span></h3></summary>
|
|
918
|
+
<div class="kv">
|
|
919
|
+
<dt>fee deployment by miner</dt><dd>above, per mined block</dd>
|
|
920
|
+
<dt>block fullness & gaps</dt><dd>block train above</dd>
|
|
921
|
+
<dt>mempool feerate landscape</dt><dd>on the <b>Mempool</b> page (fee histogram + scatter)</dd>
|
|
922
|
+
<dt>CPFP / child-pays-for-parent clusters</dt><dd>shown above, <b>for the block under construction</b>.
|
|
923
|
+
The ancestor graph is <span class="mono">getrawmempool … depends</span> (indices into the selected
|
|
924
|
+
transactions) — 1,496 transactions, 19 multi-transaction packages and 40 transactions inside a package as
|
|
925
|
+
measured on 2026-09-09. It is <b>not</b> the whole mempool: <span class="mono">getrawmempool verbose</span> on
|
|
926
|
+
this build carries no <span class="mono">depends</span> and no <span class="mono">ancestorcount</span>, and
|
|
927
|
+
there is no <span class="mono">zmqpubsequence</span>, so the unselected pool cannot be clustered. The card
|
|
928
|
+
says which of those two it is showing rather than blurring them together.</dd>
|
|
929
|
+
<dt>1p1c ancestor packages</dt><dd><span class="warn">not shown.</span> The node reports accepted/failed counts for
|
|
930
|
+
1p1c packages, but only in its own log lines, and log files are not a source this monitor reads. The package
|
|
931
|
+
view above is built from <span class="mono">getrawmempool … depends</span>, which is RPC.</dd>
|
|
932
|
+
</div>
|
|
933
|
+
</details>
|
|
934
|
+
</div>
|
|
935
|
+
</div>
|
|
936
|
+
</section>
|
|
937
|
+
|
|
938
|
+
<section class="page" data-page="logs">
|
|
939
|
+
<div class="grid">
|
|
940
|
+
<div class="card w12">
|
|
941
|
+
<h3>Monitor events <span class="sp"></span><span class="src">what this monitor observed and decided; the node's log is not a source here</span><span class="count faint tiny" id="lgCount"></span></h3>
|
|
942
|
+
<div class="rowform">
|
|
943
|
+
<input class="f grow minw-200" id="lgSearch" placeholder="filter text, e.g. orphan, unreachable, stored">
|
|
944
|
+
<select class="f" id="lgSev"><option value="">all severities</option><option value="warn">warn</option><option value="error">error</option><option value="info">info</option></select>
|
|
945
|
+
<select class="f" id="lgKind"><option value="">all kinds</option></select>
|
|
946
|
+
<span class="spacer grow"></span>
|
|
947
|
+
<button class="btn" id="lgClear">clear</button>
|
|
948
|
+
</div>
|
|
949
|
+
<div class="feed tall" id="lgFeed"></div>
|
|
950
|
+
</div>
|
|
951
|
+
</div>
|
|
952
|
+
</section>
|
|
953
|
+
|
|
954
|
+
<!-- ==================================================== NODE & RPC -->
|
|
955
|
+
<section class="page" data-page="node">
|
|
956
|
+
<div class="grid">
|
|
957
|
+
<!-- TWO STACKS (2026-09-15, "same treatment on the node & rpc tab"): the cards are of very
|
|
958
|
+
different heights -- RPC etiquette is fourteen figures and a chart, Poll cadence five
|
|
959
|
+
rows -- and as grid rows they left holes beside the short ones. Each stack packs its
|
|
960
|
+
own cards; the etiquette figures run in two columns. -->
|
|
961
|
+
<div class="ovcols ndcols">
|
|
962
|
+
<div class="ovcol">
|
|
963
|
+
<div class="card">
|
|
964
|
+
<h3>RPC etiquette <span class="sp"></span><span class="src">what this app asks of the node</span></h3>
|
|
965
|
+
<dl class="kv two" id="ndRpc"></dl>
|
|
966
|
+
<canvas class="chart short" id="ndLatChart"></canvas>
|
|
967
|
+
<div class="note tiny faint">The node's RPC server services one connection at a time on a single thread. This app keeps one request in flight, spaces them, batches multiple methods per connection, and drops a poll that could not answer in time to be true.</div>
|
|
968
|
+
</div>
|
|
969
|
+
<div class="card">
|
|
970
|
+
<h3>Data quality <span class="sp"></span><span class="src">known gaps, stated</span></h3>
|
|
971
|
+
<div id="ndQuality"></div>
|
|
972
|
+
</div>
|
|
973
|
+
<!-- THE NODE CONNECTION (operator, 2026-09-12: "Still left to do is a config connection in
|
|
974
|
+
the web settings. We have no way for users to configure a connection to their rpc
|
|
975
|
+
backend"). Test first, save second: the save button is disabled until a test has
|
|
976
|
+
actually ANSWERED, so nothing is written for an endpoint nobody reached. That is the
|
|
977
|
+
operator's own ordering.
|
|
978
|
+
No credential fields, deliberately: authentication is the node's own .cookie, found
|
|
979
|
+
from the data directory (server/config.js resolveCookie). Asking for an RPC password on
|
|
980
|
+
a page that is open access by default is not something to add quietly. -->
|
|
981
|
+
<div class="card">
|
|
982
|
+
<h3>Node connection <span class="sp"></span><span class="src">where this monitor looks</span></h3>
|
|
983
|
+
<!-- the placeholder deliberately is NOT a URL: web-contract.test.js refuses an http(s)
|
|
984
|
+
literal outside an <a href>, because in shipped markup that reads as a resource load
|
|
985
|
+
under `script-src/img-src 'self'`. It caught this exact line. -->
|
|
986
|
+
<div class="field"><label for="cnUrl">RPC URL</label><input class="f w-full" id="cnUrl" placeholder="http(s)://host:port" autocomplete="off" spellcheck="false"></div>
|
|
987
|
+
<div class="field"><label for="cnDatadir">data directory</label><input class="f w-full" id="cnDatadir" placeholder="/var/lib/bitcoin" autocomplete="off" spellcheck="false"></div>
|
|
988
|
+
<div class="rowform">
|
|
989
|
+
<select class="f" id="cnChain"><option value="main">main</option><option value="test">test</option><option value="signet">signet</option><option value="regtest">regtest</option></select>
|
|
990
|
+
<input class="f grow minw-140" id="cnLabel" placeholder="label, e.g. My node" autocomplete="off">
|
|
991
|
+
</div>
|
|
992
|
+
<div class="rowform">
|
|
993
|
+
<button class="btn" id="cnTest">test connection</button>
|
|
994
|
+
<button class="btn primary" id="cnSave" disabled>save</button>
|
|
995
|
+
</div>
|
|
996
|
+
<div class="note tiny" id="cnOut"></div>
|
|
997
|
+
<div class="note tiny faint">Authentication uses the node's <span class="mono">.cookie</span> from the data directory; no password is asked for or kept here. Saving writes this monitor's own configuration file and takes effect on restart.</div>
|
|
998
|
+
</div>
|
|
999
|
+
</div>
|
|
1000
|
+
<div class="ovcol">
|
|
1001
|
+
<div class="card">
|
|
1002
|
+
<h3>Poll cadence <span class="sp"></span><span class="src">configured vs actual</span></h3>
|
|
1003
|
+
<table class="t" id="ndCadence"><thead><tr><th>tier</th><th class="r">configured</th><th class="r">actual</th><th class="r">last run</th></tr></thead><tbody></tbody></table>
|
|
1004
|
+
<div class="note tiny faint" id="ndCadenceNote"></div>
|
|
1005
|
+
</div>
|
|
1006
|
+
<div class="ovpair">
|
|
1007
|
+
<div class="card">
|
|
1008
|
+
<h3>Monitor self-telemetry <span class="sp"></span><span class="src">this process</span></h3>
|
|
1009
|
+
<dl class="kv" id="ndSelf"></dl>
|
|
1010
|
+
<canvas class="chart short" id="ndSelfChart"></canvas>
|
|
1011
|
+
</div>
|
|
1012
|
+
<div class="card">
|
|
1013
|
+
<h3>Log tail <span class="sp"></span><span class="src">follow state</span></h3>
|
|
1014
|
+
<dl class="kv" id="ndTail"></dl>
|
|
1015
|
+
</div>
|
|
1016
|
+
</div>
|
|
1017
|
+
<div class="card">
|
|
1018
|
+
<h3>Panel sources <span class="sp"></span><span class="src">provenance</span></h3>
|
|
1019
|
+
<div id="ndSources"></div>
|
|
1020
|
+
</div>
|
|
1021
|
+
<div class="card">
|
|
1022
|
+
<h3>RPC console <span class="sp"></span><span class="src">read-only allowlist</span></h3>
|
|
1023
|
+
<div class="rowform">
|
|
1024
|
+
<input class="f grow minw-180" id="rpcMethod" placeholder="method, e.g. getblockchaininfo">
|
|
1025
|
+
<input class="f grow minw-140" id="rpcParams" placeholder='params JSON array, e.g. [6]'>
|
|
1026
|
+
<button class="btn primary" id="rpcRun">call</button>
|
|
1027
|
+
</div>
|
|
1028
|
+
<div class="note tiny faint" id="rpcNote">Mutating, wallet- and peer-control methods are refused by this app regardless of what the node would do.</div>
|
|
1029
|
+
<pre class="mono tiny output-box" id="rpcOut"></pre>
|
|
1030
|
+
</div>
|
|
1031
|
+
</div>
|
|
1032
|
+
</div>
|
|
1033
|
+
</div>
|
|
1034
|
+
</section>
|
|
1035
|
+
|
|
1036
|
+
<!-- ========================================================== ADMIN -->
|
|
1037
|
+
<section class="page" data-page="admin">
|
|
1038
|
+
<div class="grid">
|
|
1039
|
+
<!-- TWO STACKS (2026-09-15, "same treatment on the admin page"): Users beside Node actions left
|
|
1040
|
+
a hole under the short Users card; each stack packs its own. The two password fields
|
|
1041
|
+
share a row. -->
|
|
1042
|
+
<div class="ovcols adcols">
|
|
1043
|
+
<div class="ovcol">
|
|
1044
|
+
<div class="card">
|
|
1045
|
+
<h3>Users <span class="sp"></span><span class="src">scrypt-hashed, never shown</span></h3>
|
|
1046
|
+
<div class="scroll"><table class="t" id="adUsers"><thead><tr><th>user</th><th>role</th><th>created</th><th>last login</th><th></th></tr></thead><tbody></tbody></table></div>
|
|
1047
|
+
<div class="hr"></div>
|
|
1048
|
+
<div class="rowform">
|
|
1049
|
+
<input class="f" id="newUser" placeholder="username" autocomplete="off">
|
|
1050
|
+
<select class="f" id="newRole"><option value="viewer">viewer</option><option value="operator">operator</option><option value="admin">admin</option></select>
|
|
1051
|
+
<button class="btn" id="btnGen">generate password</button>
|
|
1052
|
+
</div>
|
|
1053
|
+
<div class="note tiny" id="adGen"></div>
|
|
1054
|
+
</div>
|
|
1055
|
+
<div class="card">
|
|
1056
|
+
<h3>Audit log <span class="sp"></span><span class="src">credentials use, without credentials</span></h3>
|
|
1057
|
+
<div class="scroll lg"><table class="t" id="adAudit"><thead><tr><th>when</th><th>type</th><th>who</th><th>detail</th></tr></thead><tbody></tbody></table></div>
|
|
1058
|
+
<div class="note tiny faint mt-6" id="adAuditSize"></div>
|
|
1059
|
+
</div>
|
|
1060
|
+
</div>
|
|
1061
|
+
<div class="ovcol">
|
|
1062
|
+
<div class="card">
|
|
1063
|
+
<h3>Node actions <span class="sp"></span><span class="src">off unless enabled</span></h3>
|
|
1064
|
+
<div class="note tiny" id="adActions"></div>
|
|
1065
|
+
</div>
|
|
1066
|
+
<div class="card">
|
|
1067
|
+
<h3>Change own password <span class="sp"></span></h3>
|
|
1068
|
+
<div class="ovpair">
|
|
1069
|
+
<div class="field"><label>current password</label><input class="f w-full" type="password" id="pwCurrent" autocomplete="current-password"></div>
|
|
1070
|
+
<div class="field"><label>new password</label><input class="f w-full" type="password" id="pwNew" autocomplete="new-password"></div>
|
|
1071
|
+
</div>
|
|
1072
|
+
<button class="btn primary" id="btnPw">change password</button>
|
|
1073
|
+
<div class="note tiny faint mt-6">Changing it revokes every session, including this one.</div>
|
|
1074
|
+
</div>
|
|
1075
|
+
</div>
|
|
1076
|
+
</div>
|
|
1077
|
+
</div>
|
|
1078
|
+
</section>
|
|
1079
|
+
</main>
|
|
1080
|
+
</div>
|
|
1081
|
+
|
|
1082
|
+
<div class="toast" id="toast"></div>
|
|
1083
|
+
<script type="module" src="/js/app.js"></script>
|
|
1084
|
+
</body>
|
|
1085
|
+
</html>
|