blockyard 0.0.9 → 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.
Files changed (58) hide show
  1. package/CHANGELOG.md +251 -1
  2. package/README.md +42 -23
  3. package/bin/blockyard.js +2 -1
  4. package/docs/API.md +16 -14
  5. package/docs/ARCHITECTURE.md +92 -5
  6. package/docs/CONFIGURATION.md +33 -26
  7. package/docs/GETTING-STARTED.md +5 -2
  8. package/docs/INSTALL.md +90 -33
  9. package/docs/MEASUREMENTS.md +147 -0
  10. package/docs/SECURITY.md +32 -15
  11. package/docs/TROUBLESHOOTING.md +35 -1
  12. package/docs/USER-GUIDE.md +266 -26
  13. package/package.json +1 -1
  14. package/public/404.html +1 -1
  15. package/public/css/app.css +306 -82
  16. package/public/donate-qr.png +0 -0
  17. package/public/index.html +295 -103
  18. package/public/js/agents.js +228 -51
  19. package/public/js/app.js +82 -8
  20. package/public/js/blockscene3d.js +179 -27
  21. package/public/js/charts.js +21 -21
  22. package/public/js/depthchart.js +31 -27
  23. package/public/js/details3d.js +1456 -71
  24. package/public/js/doom.js +31 -0
  25. package/public/js/dosaudio.js +48 -0
  26. package/public/js/dosgame.js +389 -0
  27. package/public/js/dosio.js +186 -0
  28. package/public/js/dospc.js +1353 -0
  29. package/public/js/dosworker.js +196 -0
  30. package/public/js/login.js +5 -0
  31. package/public/js/markets.js +46 -8
  32. package/public/js/mining.js +310 -32
  33. package/public/js/panels.js +14 -10
  34. package/public/js/pricechart.js +14 -13
  35. package/public/js/quake.js +20 -0
  36. package/public/js/settings.js +103 -21
  37. package/public/js/soundcard.js +459 -0
  38. package/public/js/theme.js +235 -0
  39. package/public/js/wolf3d.js +22 -0
  40. package/public/js/x86.js +1978 -0
  41. package/scripts/donate-qr.py +12 -9
  42. package/scripts/dos-bench.js +56 -0
  43. package/scripts/setup.js +34 -12
  44. package/scripts/shots.mjs +6 -0
  45. package/scripts/smoke.sh +1 -1
  46. package/scripts/tls.js +31 -0
  47. package/server/chain/index/build.js +21 -4
  48. package/server/collect/monitor.js +30 -1
  49. package/server/collect/network.js +295 -0
  50. package/server/config.js +46 -22
  51. package/server/http/api.js +49 -5
  52. package/server/http/games.js +77 -0
  53. package/server/http/server.js +8 -0
  54. package/server/main.js +53 -8
  55. package/server/tls/selfsigned.js +160 -0
  56. package/systemd/blockyard.service +7 -5
  57. package/docs/PRIVATE-LEADERBOARD.md +0 -230
  58. package/docs/STATE-2026-09-09.md +0 -200
Binary file
package/public/index.html CHANGED
@@ -99,10 +99,22 @@
99
99
  <button data-page="tetrust" role="menuitem" title="Tetrust: trust, but verify">Tetrust</button>
100
100
  <button data-page="blockout" role="menuitem" title="Blockout: break the wall">Blockout</button>
101
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>
102
105
  </div>
103
106
 
104
107
  <!-- Display settings (settings.js). Hidden until the gear is pressed; the contents are
105
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>
106
118
  <div class="cfgwrap hidden" id="settingsWrap">
107
119
  <div class="cfgscrim" id="settingsScrim"></div>
108
120
  <section class="cfg" id="settingsPanel" role="dialog" aria-modal="true" aria-labelledby="cfgTitle">
@@ -110,8 +122,7 @@
110
122
  <button class="btn" id="cfgReset" title="Put every setting back to the shipped default">reset</button>
111
123
  <button class="btn" id="cfgClose" aria-label="Close settings">close</button>
112
124
  </h2>
113
- <p class="cfgnote">Kept in this browser only, and applied as soon as you change them. They
114
- change what is drawn, not what is measured: every number on the page is the same either way.</p>
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>
115
126
  <div id="cfgBody"></div>
116
127
  </section>
117
128
  </div>
@@ -139,7 +150,7 @@
139
150
  is left and scrolls inside it. -->
140
151
  <div class="ovcols">
141
152
  <div class="ovcol">
142
- <div class="card w8">
153
+ <div class="card w8 flowtight">
143
154
  <h3>Block flow <span class="sp"></span><span class="src">being assembled → <b>the current block</b> → history · <a href="#mining">packages &amp; goggles</a></span></h3>
144
155
  <div id="ovTrain" class="flowwrap"></div>
145
156
  </div>
@@ -256,7 +267,7 @@
256
267
  <div class="card w4">
257
268
  <h3>Transaction rate <span class="sp"></span><span class="src">getchaintxstats txrate</span></h3>
258
269
  <canvas class="chart" id="chTxRateChart"></canvas>
259
- <div class="kv two mt-6" id="chTxStats"></div>
270
+ <div class="mksum mt-6 chtx" id="chTxStats"></div>
260
271
  </div>
261
272
 
262
273
  <div class="card w4">
@@ -333,6 +344,7 @@
333
344
  <section class="abcard"><h3>Bitcoin node</h3><div id="abNode"></div></section>
334
345
  </div>
335
346
 
347
+ <div class="ablinks">
336
348
  <a class="abgh" href="https://github.com/BobClawblaw/blockyard" target="_blank" rel="noopener noreferrer">
337
349
  <!-- The GitHub mark, GitHub's trademark, used to link to the project on GitHub. -->
338
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>
@@ -342,14 +354,17 @@
342
354
  <!-- The donation address: one click copies it (operator, 2026-09-14). A button, not a link:
343
355
  there is nowhere to go, and the address is the whole point. -->
344
356
  <button type="button" class="abdon" id="abDonate" data-copy="bc1q249cv27lc2q7y0x53vkczgfvvgsjzhwxwv42gc" title="click to copy the address">
345
- <!-- The QR is the address itself, generated once (scripts/donate-qr.py) and checked to decode back to it. -->
346
- <svg class="abqr" viewBox="0 0 41 41" width="132" height="132" role="img" aria-label="QR code of the donation address" shape-rendering="crispEdges"><rect width="41" height="41" fill="#fff"/><path fill="#000" d="M6 6h1v1h-1zM7 6h1v1h-1zM8 6h1v1h-1zM9 6h1v1h-1zM10 6h1v1h-1zM11 6h1v1h-1zM12 6h1v1h-1zM16 6h1v1h-1zM18 6h1v1h-1zM20 6h1v1h-1zM22 6h1v1h-1zM23 6h1v1h-1zM24 6h1v1h-1zM25 6h1v1h-1zM26 6h1v1h-1zM28 6h1v1h-1zM29 6h1v1h-1zM30 6h1v1h-1zM31 6h1v1h-1zM32 6h1v1h-1zM33 6h1v1h-1zM34 6h1v1h-1zM6 7h1v1h-1zM12 7h1v1h-1zM14 7h1v1h-1zM16 7h1v1h-1zM19 7h1v1h-1zM20 7h1v1h-1zM21 7h1v1h-1zM23 7h1v1h-1zM24 7h1v1h-1zM28 7h1v1h-1zM34 7h1v1h-1zM6 8h1v1h-1zM8 8h1v1h-1zM9 8h1v1h-1zM10 8h1v1h-1zM12 8h1v1h-1zM18 8h1v1h-1zM20 8h1v1h-1zM21 8h1v1h-1zM25 8h1v1h-1zM26 8h1v1h-1zM28 8h1v1h-1zM30 8h1v1h-1zM31 8h1v1h-1zM32 8h1v1h-1zM34 8h1v1h-1zM6 9h1v1h-1zM8 9h1v1h-1zM9 9h1v1h-1zM10 9h1v1h-1zM12 9h1v1h-1zM14 9h1v1h-1zM15 9h1v1h-1zM16 9h1v1h-1zM18 9h1v1h-1zM19 9h1v1h-1zM20 9h1v1h-1zM22 9h1v1h-1zM23 9h1v1h-1zM26 9h1v1h-1zM28 9h1v1h-1zM30 9h1v1h-1zM31 9h1v1h-1zM32 9h1v1h-1zM34 9h1v1h-1zM6 10h1v1h-1zM8 10h1v1h-1zM9 10h1v1h-1zM10 10h1v1h-1zM12 10h1v1h-1zM15 10h1v1h-1zM20 10h1v1h-1zM22 10h1v1h-1zM24 10h1v1h-1zM25 10h1v1h-1zM26 10h1v1h-1zM28 10h1v1h-1zM30 10h1v1h-1zM31 10h1v1h-1zM32 10h1v1h-1zM34 10h1v1h-1zM6 11h1v1h-1zM12 11h1v1h-1zM14 11h1v1h-1zM15 11h1v1h-1zM17 11h1v1h-1zM18 11h1v1h-1zM19 11h1v1h-1zM20 11h1v1h-1zM21 11h1v1h-1zM22 11h1v1h-1zM23 11h1v1h-1zM24 11h1v1h-1zM26 11h1v1h-1zM28 11h1v1h-1zM34 11h1v1h-1zM6 12h1v1h-1zM7 12h1v1h-1zM8 12h1v1h-1zM9 12h1v1h-1zM10 12h1v1h-1zM11 12h1v1h-1zM12 12h1v1h-1zM14 12h1v1h-1zM16 12h1v1h-1zM18 12h1v1h-1zM20 12h1v1h-1zM22 12h1v1h-1zM24 12h1v1h-1zM26 12h1v1h-1zM28 12h1v1h-1zM29 12h1v1h-1zM30 12h1v1h-1zM31 12h1v1h-1zM32 12h1v1h-1zM33 12h1v1h-1zM34 12h1v1h-1zM15 13h1v1h-1zM16 13h1v1h-1zM17 13h1v1h-1zM22 13h1v1h-1zM25 13h1v1h-1zM6 14h1v1h-1zM7 14h1v1h-1zM8 14h1v1h-1zM9 14h1v1h-1zM10 14h1v1h-1zM12 14h1v1h-1zM13 14h1v1h-1zM14 14h1v1h-1zM15 14h1v1h-1zM17 14h1v1h-1zM19 14h1v1h-1zM21 14h1v1h-1zM22 14h1v1h-1zM23 14h1v1h-1zM26 14h1v1h-1zM27 14h1v1h-1zM29 14h1v1h-1zM31 14h1v1h-1zM33 14h1v1h-1zM7 15h1v1h-1zM10 15h1v1h-1zM11 15h1v1h-1zM13 15h1v1h-1zM14 15h1v1h-1zM16 15h1v1h-1zM18 15h1v1h-1zM20 15h1v1h-1zM22 15h1v1h-1zM23 15h1v1h-1zM25 15h1v1h-1zM27 15h1v1h-1zM28 15h1v1h-1zM29 15h1v1h-1zM30 15h1v1h-1zM34 15h1v1h-1zM7 16h1v1h-1zM11 16h1v1h-1zM12 16h1v1h-1zM13 16h1v1h-1zM16 16h1v1h-1zM20 16h1v1h-1zM21 16h1v1h-1zM23 16h1v1h-1zM24 16h1v1h-1zM26 16h1v1h-1zM27 16h1v1h-1zM30 16h1v1h-1zM31 16h1v1h-1zM6 17h1v1h-1zM8 17h1v1h-1zM9 17h1v1h-1zM10 17h1v1h-1zM11 17h1v1h-1zM14 17h1v1h-1zM18 17h1v1h-1zM21 17h1v1h-1zM24 17h1v1h-1zM26 17h1v1h-1zM28 17h1v1h-1zM29 17h1v1h-1zM7 18h1v1h-1zM8 18h1v1h-1zM9 18h1v1h-1zM10 18h1v1h-1zM11 18h1v1h-1zM12 18h1v1h-1zM13 18h1v1h-1zM14 18h1v1h-1zM15 18h1v1h-1zM16 18h1v1h-1zM18 18h1v1h-1zM19 18h1v1h-1zM23 18h1v1h-1zM26 18h1v1h-1zM27 18h1v1h-1zM31 18h1v1h-1zM32 18h1v1h-1zM33 18h1v1h-1zM8 19h1v1h-1zM9 19h1v1h-1zM10 19h1v1h-1zM11 19h1v1h-1zM13 19h1v1h-1zM14 19h1v1h-1zM20 19h1v1h-1zM22 19h1v1h-1zM24 19h1v1h-1zM25 19h1v1h-1zM28 19h1v1h-1zM29 19h1v1h-1zM30 19h1v1h-1zM34 19h1v1h-1zM7 20h1v1h-1zM8 20h1v1h-1zM9 20h1v1h-1zM11 20h1v1h-1zM12 20h1v1h-1zM13 20h1v1h-1zM14 20h1v1h-1zM17 20h1v1h-1zM18 20h1v1h-1zM19 20h1v1h-1zM21 20h1v1h-1zM23 20h1v1h-1zM27 20h1v1h-1zM29 20h1v1h-1zM32 20h1v1h-1zM33 20h1v1h-1zM7 21h1v1h-1zM9 21h1v1h-1zM11 21h1v1h-1zM13 21h1v1h-1zM14 21h1v1h-1zM17 21h1v1h-1zM27 21h1v1h-1zM28 21h1v1h-1zM29 21h1v1h-1zM30 21h1v1h-1zM34 21h1v1h-1zM8 22h1v1h-1zM9 22h1v1h-1zM10 22h1v1h-1zM11 22h1v1h-1zM12 22h1v1h-1zM17 22h1v1h-1zM19 22h1v1h-1zM20 22h1v1h-1zM22 22h1v1h-1zM23 22h1v1h-1zM25 22h1v1h-1zM26 22h1v1h-1zM29 22h1v1h-1zM31 22h1v1h-1zM32 22h1v1h-1zM33 22h1v1h-1zM6 23h1v1h-1zM10 23h1v1h-1zM11 23h1v1h-1zM13 23h1v1h-1zM14 23h1v1h-1zM15 23h1v1h-1zM18 23h1v1h-1zM22 23h1v1h-1zM24 23h1v1h-1zM25 23h1v1h-1zM26 23h1v1h-1zM28 23h1v1h-1zM30 23h1v1h-1zM31 23h1v1h-1zM33 23h1v1h-1zM34 23h1v1h-1zM6 24h1v1h-1zM8 24h1v1h-1zM10 24h1v1h-1zM12 24h1v1h-1zM13 24h1v1h-1zM14 24h1v1h-1zM19 24h1v1h-1zM20 24h1v1h-1zM21 24h1v1h-1zM23 24h1v1h-1zM27 24h1v1h-1zM6 25h1v1h-1zM9 25h1v1h-1zM10 25h1v1h-1zM13 25h1v1h-1zM16 25h1v1h-1zM18 25h1v1h-1zM20 25h1v1h-1zM24 25h1v1h-1zM30 25h1v1h-1zM33 25h1v1h-1zM34 25h1v1h-1zM6 26h1v1h-1zM7 26h1v1h-1zM8 26h1v1h-1zM12 26h1v1h-1zM13 26h1v1h-1zM14 26h1v1h-1zM15 26h1v1h-1zM18 26h1v1h-1zM19 26h1v1h-1zM20 26h1v1h-1zM23 26h1v1h-1zM26 26h1v1h-1zM27 26h1v1h-1zM28 26h1v1h-1zM29 26h1v1h-1zM30 26h1v1h-1zM31 26h1v1h-1zM32 26h1v1h-1zM33 26h1v1h-1zM14 27h1v1h-1zM22 27h1v1h-1zM25 27h1v1h-1zM26 27h1v1h-1zM30 27h1v1h-1zM32 27h1v1h-1zM33 27h1v1h-1zM34 27h1v1h-1zM6 28h1v1h-1zM7 28h1v1h-1zM8 28h1v1h-1zM9 28h1v1h-1zM10 28h1v1h-1zM11 28h1v1h-1zM12 28h1v1h-1zM14 28h1v1h-1zM16 28h1v1h-1zM17 28h1v1h-1zM18 28h1v1h-1zM19 28h1v1h-1zM21 28h1v1h-1zM22 28h1v1h-1zM23 28h1v1h-1zM25 28h1v1h-1zM26 28h1v1h-1zM28 28h1v1h-1zM30 28h1v1h-1zM31 28h1v1h-1zM6 29h1v1h-1zM12 29h1v1h-1zM15 29h1v1h-1zM17 29h1v1h-1zM20 29h1v1h-1zM24 29h1v1h-1zM26 29h1v1h-1zM30 29h1v1h-1zM31 29h1v1h-1zM33 29h1v1h-1zM34 29h1v1h-1zM6 30h1v1h-1zM8 30h1v1h-1zM9 30h1v1h-1zM10 30h1v1h-1zM12 30h1v1h-1zM14 30h1v1h-1zM17 30h1v1h-1zM21 30h1v1h-1zM22 30h1v1h-1zM23 30h1v1h-1zM26 30h1v1h-1zM27 30h1v1h-1zM28 30h1v1h-1zM29 30h1v1h-1zM30 30h1v1h-1zM32 30h1v1h-1zM33 30h1v1h-1zM34 30h1v1h-1zM6 31h1v1h-1zM8 31h1v1h-1zM9 31h1v1h-1zM10 31h1v1h-1zM12 31h1v1h-1zM14 31h1v1h-1zM18 31h1v1h-1zM19 31h1v1h-1zM22 31h1v1h-1zM25 31h1v1h-1zM27 31h1v1h-1zM29 31h1v1h-1zM33 31h1v1h-1zM34 31h1v1h-1zM6 32h1v1h-1zM8 32h1v1h-1zM9 32h1v1h-1zM10 32h1v1h-1zM12 32h1v1h-1zM14 32h1v1h-1zM16 32h1v1h-1zM19 32h1v1h-1zM20 32h1v1h-1zM21 32h1v1h-1zM22 32h1v1h-1zM23 32h1v1h-1zM26 32h1v1h-1zM28 32h1v1h-1zM30 32h1v1h-1zM31 32h1v1h-1zM32 32h1v1h-1zM33 32h1v1h-1zM6 33h1v1h-1zM12 33h1v1h-1zM14 33h1v1h-1zM15 33h1v1h-1zM18 33h1v1h-1zM19 33h1v1h-1zM22 33h1v1h-1zM24 33h1v1h-1zM25 33h1v1h-1zM26 33h1v1h-1zM31 33h1v1h-1zM33 33h1v1h-1zM6 34h1v1h-1zM7 34h1v1h-1zM8 34h1v1h-1zM9 34h1v1h-1zM10 34h1v1h-1zM11 34h1v1h-1zM12 34h1v1h-1zM14 34h1v1h-1zM16 34h1v1h-1zM18 34h1v1h-1zM20 34h1v1h-1zM21 34h1v1h-1zM23 34h1v1h-1zM25 34h1v1h-1zM28 34h1v1h-1zM29 34h1v1h-1zM30 34h1v1h-1zM31 34h1v1h-1zM32 34h1v1h-1z"/></svg>
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">
347
361
  <span class="abdontext">
348
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>
349
363
  <code>bc1q249cv27lc2q7y0x53vkczgfvvgsjzhwxwv42gc</code>
350
364
  <span class="abdonhint">scan, or click to copy</span>
351
365
  </span>
352
366
  </button>
367
+ </div>
353
368
 
354
369
  <p class="abcredit">Entirely made by machines. Directed by human hands.</p>
355
370
  </div>
@@ -465,12 +480,13 @@
465
480
  <div class="khead"><b>Block space</b><span class="faint">the next block's worth of the mempool</span></div>
466
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>
467
482
  </div>
468
- <div class="kpanel kbf">
469
- <div class="khead"><b>Block flow</b><span class="faint">projected being built the chain</span></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>
470
487
  <div id="kTrain" class="flowwrap"></div>
471
488
  </div>
472
489
  </div>
473
- <button type="button" class="kfull" id="kFull" title="full screen">full screen</button>
474
490
  </div>
475
491
  </section>
476
492
  <!-- TETRUST: a Tetris on the 3D engine (operator, 2026-09-12). The well is a board3d, so it wears
@@ -589,6 +605,110 @@
589
605
  </div>
590
606
  </section>
591
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
+
592
712
  <!-- ======================================================== MARKETS -->
593
713
  <!-- Exchange prices on the block-space board (public/js/markets.js, server/collect/markets.js). -->
594
714
  <section class="page" data-page="markets">
@@ -618,12 +738,22 @@
618
738
  <div class="treemapwrap mkboard"><canvas class="treemap" id="mkBoard"></canvas><div class="goggles-tip hidden"></div></div>
619
739
  </div>
620
740
  <canvas class="mkchart" id="mkChart"></canvas>
621
- <div id="mkTable"></div>
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">
622
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>
623
752
  <div class="mkbar" id="mkDepthBar"></div>
624
753
  <canvas class="mkchart mkdepth" id="mkDepth"></canvas>
625
754
  <div class="note tiny faint" id="mkDepthNote"></div>
626
- <div class="note tiny faint" id="mkNote"></div>
755
+ </div>
756
+ </div>
627
757
  </div>
628
758
  </div>
629
759
  </section>
@@ -673,20 +803,29 @@
673
803
  <!-- ======================================================== NETWORK -->
674
804
  <section class="page" data-page="network">
675
805
  <div class="grid">
676
- <div class="card w6">
677
- <h3>Throughput <span class="sp"></span><span class="src">getnettotals (RPC)</span></h3>
678
- <div class="metric"><div class="l">network in</div><div><span class="v" id="ovNetIn">–</span><span class="u" id="ovNetInU"></span></div>
679
- <div class="s" id="ovDisk">–</div></div>
680
- <canvas class="chart" id="ovNetChart"></canvas>
681
- </div>
682
- <div class="card w6">
683
- <h3>Upload <span class="sp"></span><span class="src">what the node reports</span></h3>
684
- <div class="note" id="ntUploadNote"></div>
685
- <dl class="kv" id="ntRpc"></dl>
686
- </div>
687
- <div class="card w6">
688
- <h3>Traffic accounting <span class="sp"></span><span class="src">cumulative</span></h3>
689
- <dl class="kv" id="ntAccounting"></dl>
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>
690
829
  </div>
691
830
  <div class="card w12">
692
831
  <h3>Where each number comes from <span class="sp"></span><span class="src">explicit sources</span></h3>
@@ -698,47 +837,83 @@
698
837
  <!-- ========================================================= EVENTS -->
699
838
  <section class="page" data-page="mining">
700
839
  <div class="grid">
701
- <!-- TWO INDEPENDENT STACKS (operator, 2026-09-11: "too much wasted space in this
702
- display. Crunch down the panel sizes to fit more info"). One grid coupled the rows:
703
- the square Mempool space viewer set the first row, so Block flow sat in a 591 px
704
- card using ~273, and the landscape was stretched to the packages table. Stacks never
705
- stretch a card to its neighbour; the explanatory Detailed card folds away. -->
706
- <div class="mncols">
707
- <div class="ovcol">
708
- <div class="card">
709
- <h3>Block flow <span class="sp"></span><span class="src">being assembled <b>the current block</b> history receding</span></h3>
710
- <div id="mnFlow" class="flowwrap"></div>
711
- </div>
712
- <div class="mnpair">
713
- <div class="card">
714
- <h3>Packages in the block being built <span class="sp"></span><span class="src">ancestor graph from getrawmempool · depends</span></h3>
715
- <div id="mnPackages"></div>
716
- </div>
717
- <div class="card">
718
- <h3>Feerate landscape <span class="sp"></span><span class="src">the transactions selected for the next block</span></h3>
719
- <canvas class="chart" id="mnFeeLandscape"></canvas>
720
- <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 still fits.</div>
721
- </div>
722
- </div>
723
- <div class="card">
724
- <h3>Pools in this window <span class="sp"></span><span class="src">coinbase tags, grouped by curated label</span></h3>
725
- <div id="mnPools"></div>
726
- </div>
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>
727
867
  </div>
728
- <div class="ovcol">
729
- <div class="card">
730
- <h3>Mempool space <span class="sp"></span><span class="src">every rectangle is a waiting transaction: area = vbytes, colour = its feerate, richest first</span></h3>
731
- <div class="treemapwrap sq">
732
- <canvas class="treemap" id="gnMempoolTreemap"></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>
733
- <div class="goggles-tip hidden"></div>
734
- </div>
735
- <div class="note tiny faint" id="gnMempoolNote"></div>
736
- </div>
737
- <div class="card">
738
- <h3>Attribution <span class="sp"></span><span class="src">what we read and from where</span></h3>
739
- <div id="mnCoverage"></div>
740
- </div>
741
- <details class="card mndetailed">
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 &amp; 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">
742
917
  <summary><h3>Detailed <span class="sp"></span><span class="src">what this node can and cannot answer</span></h3></summary>
743
918
  <div class="kv">
744
919
  <dt>fee deployment by miner</dt><dd>above, per mined block</dd>
@@ -756,7 +931,6 @@
756
931
  view above is built from <span class="mono">getrawmempool … depends</span>, which is RPC.</dd>
757
932
  </div>
758
933
  </details>
759
- </div>
760
934
  </div>
761
935
  </div>
762
936
  </section>
@@ -780,36 +954,22 @@
780
954
  <!-- ==================================================== NODE & RPC -->
781
955
  <section class="page" data-page="node">
782
956
  <div class="grid">
783
- <div class="card w6">
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">
784
964
  <h3>RPC etiquette <span class="sp"></span><span class="src">what this app asks of the node</span></h3>
785
- <dl class="kv" id="ndRpc"></dl>
965
+ <dl class="kv two" id="ndRpc"></dl>
786
966
  <canvas class="chart short" id="ndLatChart"></canvas>
787
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>
788
968
  </div>
789
- <div class="card w6">
790
- <h3>Poll cadence <span class="sp"></span><span class="src">configured vs actual</span></h3>
791
- <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>
792
- <div class="note tiny faint" id="ndCadenceNote"></div>
793
- </div>
794
-
795
- <div class="card w6">
969
+ <div class="card">
796
970
  <h3>Data quality <span class="sp"></span><span class="src">known gaps, stated</span></h3>
797
971
  <div id="ndQuality"></div>
798
972
  </div>
799
- <div class="card w3">
800
- <h3>Monitor self-telemetry <span class="sp"></span><span class="src">this process</span></h3>
801
- <dl class="kv" id="ndSelf"></dl>
802
- <canvas class="chart short" id="ndSelfChart"></canvas>
803
- </div>
804
- <div class="card w3">
805
- <h3>Log tail <span class="sp"></span><span class="src">follow state</span></h3>
806
- <dl class="kv" id="ndTail"></dl>
807
- </div>
808
-
809
- <div class="card w6">
810
- <h3>Panel sources <span class="sp"></span><span class="src">provenance</span></h3>
811
- <div id="ndSources"></div>
812
- </div>
813
973
  <!-- THE NODE CONNECTION (operator, 2026-09-12: "Still left to do is a config connection in
814
974
  the web settings. We have no way for users to configure a connection to their rpc
815
975
  backend"). Test first, save second: the save button is disabled until a test has
@@ -818,7 +978,7 @@
818
978
  No credential fields, deliberately: authentication is the node's own .cookie, found
819
979
  from the data directory (server/config.js resolveCookie). Asking for an RPC password on
820
980
  a page that is open access by default is not something to add quietly. -->
821
- <div class="card w6">
981
+ <div class="card">
822
982
  <h3>Node connection <span class="sp"></span><span class="src">where this monitor looks</span></h3>
823
983
  <!-- the placeholder deliberately is NOT a URL: web-contract.test.js refuses an http(s)
824
984
  literal outside an <a href>, because in shipped markup that reads as a resource load
@@ -836,7 +996,29 @@
836
996
  <div class="note tiny" id="cnOut"></div>
837
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>
838
998
  </div>
839
- <div class="card w6">
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">
840
1022
  <h3>RPC console <span class="sp"></span><span class="src">read-only allowlist</span></h3>
841
1023
  <div class="rowform">
842
1024
  <input class="f grow minw-180" id="rpcMethod" placeholder="method, e.g. getblockchaininfo">
@@ -846,13 +1028,20 @@
846
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>
847
1029
  <pre class="mono tiny output-box" id="rpcOut"></pre>
848
1030
  </div>
1031
+ </div>
1032
+ </div>
849
1033
  </div>
850
1034
  </section>
851
1035
 
852
1036
  <!-- ========================================================== ADMIN -->
853
1037
  <section class="page" data-page="admin">
854
1038
  <div class="grid">
855
- <div class="card w6">
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">
856
1045
  <h3>Users <span class="sp"></span><span class="src">scrypt-hashed, never shown</span></h3>
857
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>
858
1047
  <div class="hr"></div>
@@ -863,25 +1052,28 @@
863
1052
  </div>
864
1053
  <div class="note tiny" id="adGen"></div>
865
1054
  </div>
866
-
867
- <div class="card w6">
868
- <h3>Node actions <span class="sp"></span><span class="src">off unless enabled</span></h3>
869
- <div class="note tiny" id="adActions"></div>
870
- </div>
871
-
872
- <div class="card w6">
1055
+ <div class="card">
873
1056
  <h3>Audit log <span class="sp"></span><span class="src">credentials use, without credentials</span></h3>
874
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>
875
1058
  <div class="note tiny faint mt-6" id="adAuditSize"></div>
876
1059
  </div>
877
-
878
- <div class="card w6">
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">
879
1067
  <h3>Change own password <span class="sp"></span></h3>
880
- <div class="field"><label>current password</label><input class="f w-full" type="password" id="pwCurrent" autocomplete="current-password"></div>
881
- <div class="field"><label>new password</label><input class="f w-full" type="password" id="pwNew" autocomplete="new-password"></div>
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>
882
1072
  <button class="btn primary" id="btnPw">change password</button>
883
1073
  <div class="note tiny faint mt-6">Changing it revokes every session, including this one.</div>
884
1074
  </div>
1075
+ </div>
1076
+ </div>
885
1077
  </div>
886
1078
  </section>
887
1079
  </main>