blockyard 0.0.9 → 0.1.1

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 (97) hide show
  1. package/CHANGELOG.md +359 -1
  2. package/README.md +48 -27
  3. package/SECURITY.md +2 -2
  4. package/bin/blockyard.js +2 -1
  5. package/docs/API.md +17 -15
  6. package/docs/ARCHITECTURE.md +128 -10
  7. package/docs/CONFIGURATION.md +39 -30
  8. package/docs/DEFECTS.md +4 -1
  9. package/docs/GETTING-STARTED.md +18 -8
  10. package/docs/INSTALL.md +97 -37
  11. package/docs/MEASUREMENTS.md +147 -0
  12. package/docs/PLAN-SCORCHED-YARD.md +456 -0
  13. package/docs/PLAN-SKIES.md +142 -0
  14. package/docs/SECURITY-AUDIT-2026-09-16.md +647 -0
  15. package/docs/SECURITY.md +58 -22
  16. package/docs/TROUBLESHOOTING.md +44 -5
  17. package/docs/USER-GUIDE.md +505 -35
  18. package/package.json +4 -2
  19. package/public/404.html +1 -1
  20. package/public/css/app.css +393 -82
  21. package/public/donate-qr.png +0 -0
  22. package/public/index.html +353 -109
  23. package/public/js/agents.js +228 -51
  24. package/public/js/app.js +131 -16
  25. package/public/js/blockanoid.js +15 -7
  26. package/public/js/blockout.js +15 -7
  27. package/public/js/blockscene3d.js +230 -38
  28. package/public/js/charts.js +21 -21
  29. package/public/js/depthchart.js +31 -27
  30. package/public/js/details3d.js +1481 -73
  31. package/public/js/doom.js +31 -0
  32. package/public/js/dosaudio.js +48 -0
  33. package/public/js/dosgame.js +389 -0
  34. package/public/js/dosio.js +186 -0
  35. package/public/js/dospc.js +1353 -0
  36. package/public/js/dosworker.js +196 -0
  37. package/public/js/explorer.js +7 -1
  38. package/public/js/livingsky.js +494 -0
  39. package/public/js/login.js +8 -2
  40. package/public/js/markets.js +46 -8
  41. package/public/js/mining.js +314 -36
  42. package/public/js/panels.js +41 -28
  43. package/public/js/pricechart.js +14 -13
  44. package/public/js/quake.js +20 -0
  45. package/public/js/safenext.js +14 -0
  46. package/public/js/scorched.js +1051 -0
  47. package/public/js/scorchedai.js +227 -0
  48. package/public/js/scorchedair.js +286 -0
  49. package/public/js/scorchedfx.js +376 -0
  50. package/public/js/scorchedshop.js +105 -0
  51. package/public/js/scorchedwind.js +69 -0
  52. package/public/js/scorchedyard.js +1338 -0
  53. package/public/js/settings.js +368 -100
  54. package/public/js/soundcard.js +459 -0
  55. package/public/js/tetrust.js +15 -6
  56. package/public/js/tetsound.js +35 -5
  57. package/public/js/theme.js +235 -0
  58. package/public/js/wolf3d.js +22 -0
  59. package/public/js/x86.js +1978 -0
  60. package/scripts/check.js +46 -0
  61. package/scripts/donate-qr.py +12 -9
  62. package/scripts/dos-bench.js +56 -0
  63. package/scripts/index-build.js +9 -2
  64. package/scripts/pool-map.js +152 -36
  65. package/scripts/setup.js +142 -22
  66. package/scripts/shots.mjs +27 -0
  67. package/scripts/smoke.sh +7 -6
  68. package/scripts/tls.js +31 -0
  69. package/scripts/ui.js +4 -2
  70. package/server/auth/sessions.js +33 -13
  71. package/server/chain/blockfile.js +64 -5
  72. package/server/chain/index/build.js +451 -58
  73. package/server/chain/index/heights.js +29 -3
  74. package/server/chain/index/live.js +13 -7
  75. package/server/chain/index/rows.js +6 -1
  76. package/server/chain/index/store.js +28 -5
  77. package/server/chain/index/worker.js +23 -11
  78. package/server/collect/logparse.js +65 -18
  79. package/server/collect/markets.js +76 -7
  80. package/server/collect/mining.js +32 -0
  81. package/server/collect/monitor.js +54 -12
  82. package/server/collect/network.js +305 -0
  83. package/server/config.js +53 -22
  84. package/server/http/api.js +119 -18
  85. package/server/http/games.js +77 -0
  86. package/server/http/server.js +30 -5
  87. package/server/http/sse.js +53 -7
  88. package/server/main.js +66 -11
  89. package/server/rpc/allowlist.js +26 -0
  90. package/server/rpc/client.js +30 -2
  91. package/server/store/audit.js +6 -1
  92. package/server/store/history.js +19 -3
  93. package/server/store/ledger.js +15 -4
  94. package/server/tls/selfsigned.js +160 -0
  95. package/systemd/blockyard.service +41 -8
  96. package/docs/PRIVATE-LEADERBOARD.md +0 -230
  97. package/docs/STATE-2026-09-09.md +0 -200
@@ -10,15 +10,15 @@
10
10
  // gives 500 levels, OKX 5000 -- a percent or two either side) is not extended: its line ends,
11
11
  // the end is marked, and the total past it counts only the books that reach.
12
12
  import { niceTicks } from './charts.js';
13
+ import { INK } from './theme.js'; // the axis, grid and tooltip colours of the chosen theme
13
14
  import { prep } from './pricechart.js';
14
15
 
15
16
  export const AGOS = [[60, '1m'], [300, '5m'], [600, '10m'], [1800, '30m'], [3600, '1h']];
16
17
  export const ZOOMS = [[0.01, '±1%'], [0.025, '±2.5%'], [0.05, '±5%'], [0.1, '±10%']];
17
18
  export const DEPTH_MS = 30_000;
18
19
  const D = { data: null, at: 0, busy: false, key: null, ago: 600, zoom: 0.05, hover: null, bound: false, error: null };
19
- const BID = '#3dff7a', ASK = '#ff4545';
20
- const BID_T = 'rgba(61,255,122,0.32)', ASK_T = 'rgba(255,69,69,0.32)';
21
- const BID_THEN = 'rgba(61,255,122,0.55)', ASK_THEN = 'rgba(255,69,69,0.55)';
20
+ // the two sides' colours come from the theme's ink (theme.js, 2026-09-16): neon green and red on a
21
+ // dark ground, the theme's own good and bad on a light one, where neon has no contrast
22
22
  const PAD_FULL = { top: 28, right: 62, bottom: 22, left: 56 };
23
23
  // COMPACT, for the kiosk. The full chart spends 62 px on the right purely to label the change
24
24
  // bars' axis and 56 on the left for the cumulative-BTC labels plus a rotated caption -- on a wall
@@ -90,13 +90,13 @@ export function depthSums(ser, pct) {
90
90
  return { bids: ser.bid[ib] ?? null, asks: ser.ask[ia] ?? null, atLeast: Boolean(ser.bidPart?.[ib] || ser.askPart?.[ia]) };
91
91
  }
92
92
 
93
- export function drawDepth(canvas, ser, { zoom = 0.05, hover = null, compact = false } = {}) {
93
+ export function drawDepth(canvas, ser, { zoom = 0.05, hover = null, compact = false, empty = null } = {}) {
94
94
  if (!canvas?.getContext) return null;
95
95
  const PAD = compact ? PAD_COMPACT : PAD_FULL;
96
96
  const { ctx, w, h } = prep(canvas);
97
97
  if (!ser) {
98
- ctx.fillStyle = '#6a7484'; ctx.textAlign = 'center';
99
- ctx.fillText('asking the exchanges for their order books…', w / 2, h / 2);
98
+ ctx.fillStyle = INK.text; ctx.textAlign = 'center';
99
+ ctx.fillText(empty ?? 'asking the exchanges for their order books…', w / 2, h / 2);
100
100
  return null;
101
101
  }
102
102
  const plotW = Math.max(10, w - PAD.left - PAD.right), plotH = Math.max(40, h - PAD.top - PAD.bottom);
@@ -120,13 +120,13 @@ export function drawDepth(canvas, ser, { zoom = 0.05, hover = null, compact = fa
120
120
  for (const v of niceTicks(0, vmax, Math.max(3, Math.floor(plotH / 44)))) {
121
121
  const y = Math.round(Y(v)) + 0.5;
122
122
  if (y < PAD.top) continue;
123
- ctx.strokeStyle = '#1a2029'; line(PAD.left, y, w - PAD.right, y);
124
- ctx.fillStyle = '#7d8898'; ctx.fillText(btc(v), PAD.left - 6, y);
123
+ ctx.strokeStyle = INK.grid; line(PAD.left, y, w - PAD.right, y);
124
+ ctx.fillStyle = INK.axisText; ctx.fillText(btc(v), PAD.left - 6, y);
125
125
  }
126
126
  if (!compact) {
127
127
  ctx.save?.();
128
128
  ctx.translate(12, PAD.top + plotH / 2); ctx.rotate(-Math.PI / 2);
129
- ctx.textAlign = 'center'; ctx.fillStyle = '#56606e'; ctx.fillText('BTC, cumulative', 0, 0);
129
+ ctx.textAlign = 'center'; ctx.fillStyle = INK.textDim; ctx.fillText('BTC, cumulative', 0, 0);
130
130
  ctx.restore?.();
131
131
  }
132
132
  ctx.setTransform(Math.min(globalThis.devicePixelRatio || 1, 2), 0, 0, Math.min(globalThis.devicePixelRatio || 1, 2), 0, 0);
@@ -137,10 +137,10 @@ export function drawDepth(canvas, ser, { zoom = 0.05, hover = null, compact = fa
137
137
  const v = e === 0 ? 0 : Math.sign(e) * 10 ** Math.abs(e);
138
138
  const y = Math.round(YC(v)) + 0.5;
139
139
  if (!compact) {
140
- ctx.fillStyle = '#7d8898';
140
+ ctx.fillStyle = INK.axisText;
141
141
  ctx.fillText(v === 0 ? '0' : `${v > 0 ? '' : '-'}${shortN(Math.abs(v))}`, w - PAD.right + 6, y);
142
142
  }
143
- if (v === 0) { ctx.strokeStyle = '#2a323d'; line(PAD.left, y, w - PAD.right, y); }
143
+ if (v === 0) { ctx.strokeStyle = INK.axis; line(PAD.left, y, w - PAD.right, y); }
144
144
  }
145
145
  }
146
146
  // price axis
@@ -148,10 +148,10 @@ export function drawDepth(canvas, ser, { zoom = 0.05, hover = null, compact = fa
148
148
  for (const p of niceTicks(pmin, pmax, Math.max(3, Math.floor(plotW / 90)))) {
149
149
  const x = Math.round(X(p)) + 0.5;
150
150
  if (x < PAD.left || x > w - PAD.right) continue;
151
- ctx.strokeStyle = '#161b22'; line(x, PAD.top, x, PAD.top + plotH);
152
- ctx.fillStyle = '#7d8898'; ctx.fillText(zoom <= 0.01 ? money(p) : kUsd(p), x, h - PAD.bottom / 2);
151
+ ctx.strokeStyle = INK.grid; line(x, PAD.top, x, PAD.top + plotH);
152
+ ctx.fillStyle = INK.axisText; ctx.fillText(zoom <= 0.01 ? money(p) : kUsd(p), x, h - PAD.bottom / 2);
153
153
  }
154
- ctx.strokeStyle = '#2a323d';
154
+ ctx.strokeStyle = INK.axis;
155
155
  line(PAD.left + 0.5, PAD.top, PAD.left + 0.5, PAD.top + plotH);
156
156
  line(w - PAD.right + 0.5, PAD.top, w - PAD.right + 0.5, PAD.top + plotH);
157
157
  line(PAD.left, PAD.top + plotH + 0.5, w - PAD.right, PAD.top + plotH + 0.5);
@@ -180,15 +180,15 @@ export function drawDepth(canvas, ser, { zoom = 0.05, hover = null, compact = fa
180
180
  ctx.stroke();
181
181
  ctx.setLineDash([]); ctx.lineWidth = 1;
182
182
  };
183
- for (const e of ser.exchanges) { curve(e.bids, BID_T, 1); curve(e.asks, ASK_T, 1); }
184
- if (ser.change) { curve(ser.thenBid, BID_THEN, 1.4, [5, 4]); curve(ser.thenAsk, ASK_THEN, 1.4, [5, 4]); }
183
+ for (const e of ser.exchanges) { curve(e.bids, INK.bidT, 1); curve(e.asks, INK.askT, 1); }
184
+ if (ser.change) { curve(ser.thenBid, INK.bidThen, 1.4, [5, 4]); curve(ser.thenAsk, INK.askThen, 1.4, [5, 4]); }
185
185
  const only = (arr, part, want) => arr.map((v, i) => (v != null && (part[i] === want || (i > 0 && part[i - 1] === want) || (i < arr.length - 1 && part[i + 1] === want)) ? v : null));
186
- curve(only(ser.bid, ser.bidPart, true), 'rgba(61,255,122,0.5)', 2.2, [2, 3]);
187
- curve(only(ser.ask, ser.askPart, true), 'rgba(255,69,69,0.5)', 2.2, [2, 3]);
188
- curve(only(ser.bid, ser.bidPart, false), BID, 2.2);
189
- curve(only(ser.ask, ser.askPart, false), ASK, 2.2);
186
+ curve(only(ser.bid, ser.bidPart, true), INK.bidHalf, 2.2, [2, 3]);
187
+ curve(only(ser.ask, ser.askPart, true), INK.askHalf, 2.2, [2, 3]);
188
+ curve(only(ser.bid, ser.bidPart, false), INK.bid, 2.2);
189
+ curve(only(ser.ask, ser.askPart, false), INK.ask, 2.2);
190
190
  // the mid, and where each shallow book ends
191
- ctx.strokeStyle = 'rgba(220,230,240,0.35)';
191
+ ctx.strokeStyle = INK.dash;
192
192
  const xm = Math.round(X(ser.mid)) + 0.5;
193
193
  line(xm, PAD.top, xm, PAD.top + plotH);
194
194
  ctx.font = '10px ui-monospace, SFMono-Regular, Menlo, monospace';
@@ -197,9 +197,9 @@ export function drawDepth(canvas, ser, { zoom = 0.05, hover = null, compact = fa
197
197
  for (const p of [r.low, r.high]) {
198
198
  if (p == null || p < pmin || p > pmax) continue;
199
199
  const x = Math.round(X(p)) + 0.5;
200
- ctx.strokeStyle = 'rgba(200,210,225,0.5)'; line(x, PAD.top + plotH - 8, x, PAD.top + plotH);
200
+ ctx.strokeStyle = INK.dash; line(x, PAD.top + plotH - 8, x, PAD.top + plotH);
201
201
  if (!compact) {
202
- ctx.fillStyle = '#8994a3'; ctx.textAlign = 'center';
202
+ ctx.fillStyle = INK.label; ctx.textAlign = 'center';
203
203
  ctx.fillText(`${r.name} ends`, x, PAD.top + plotH - 14 - 11 * (lane++ % 3));
204
204
  }
205
205
  }
@@ -207,14 +207,14 @@ export function drawDepth(canvas, ser, { zoom = 0.05, hover = null, compact = fa
207
207
  ctx.font = '11px ui-monospace, SFMono-Regular, Menlo, monospace';
208
208
 
209
209
  // readout: the pointer's price, or the sums near the mid
210
- ctx.textAlign = 'left'; ctx.fillStyle = '#dfe6ee';
210
+ ctx.textAlign = 'left'; ctx.fillStyle = INK.bright;
211
211
  if (hover && hover.x >= PAD.left && hover.x <= w - PAD.right) {
212
212
  const p = pmin + ((hover.x - PAD.left) / plotW) * (pmax - pmin);
213
213
  const i = Math.max(0, Math.min(ser.prices.length - 1, Math.round((p - ser.prices[0]) / ser.step)));
214
214
  const bidSide = ser.prices[i] <= ser.mid;
215
215
  const cum = bidSide ? ser.bid[i] : ser.ask[i];
216
216
  const per = ser.exchanges.map((e) => [e.name, bidSide ? e.bids[i] : e.asks?.[i]]).filter(([, v]) => v != null).map(([nm, v]) => `${nm} ${btc(v)}`).join(' · ');
217
- ctx.strokeStyle = 'rgba(200,215,230,0.5)'; ctx.setLineDash([3, 3]);
217
+ ctx.strokeStyle = INK.dash; ctx.setLineDash([3, 3]);
218
218
  const x = Math.round(X(ser.prices[i])) + 0.5;
219
219
  line(x, PAD.top, x, PAD.top + plotH);
220
220
  ctx.setLineDash([]);
@@ -237,8 +237,12 @@ export function depthBarHtml(ago = D.ago, zoom = D.zoom) {
237
237
  <div class="mkgrp"><span class="mklab">price window</span>${ZOOMS.map(([z, l]) => b('data-zoom', z, l, z === zoom)).join('')}</div>`;
238
238
  }
239
239
 
240
+ // what the empty chart says while the feed is off: the switch, or the server's hard off
241
+ const offText = (d) => (d?.enabled === false ? (d.polling === false ? 'market polling is off — Display settings → Markets & Price → Enable market polling' : 'market data is off on this server') : null);
242
+
240
243
  export function depthNote(d, fmt) {
241
244
  if (!d) return 'asking the exchanges for their order books…';
245
+ if (d.enabled === false) return d.note != null ? fmt.esc(d.note) : 'market data is off on this monitor'; // markup sink: escaped (audit 2026-09-16, I2)
242
246
  if (d.warming) return 'reading the order books for the first time…';
243
247
  const agoL = AGOS.find(([s]) => s === d.ago)?.[1] ?? `${d.ago} s`;
244
248
  const parts = [`books read at ${clock(d.at)} UTC, every ${Math.round((d.bookMs ?? DEPTH_MS) / 1000)} s while this tab is open`];
@@ -252,7 +256,7 @@ export function depthNote(d, fmt) {
252
256
  function draw() {
253
257
  const canvas = document.getElementById('mkDepth');
254
258
  if (!canvas) return;
255
- drawDepth(canvas, depthSeries(D.data), { zoom: D.zoom, hover: D.hover });
259
+ drawDepth(canvas, depthSeries(D.data), { zoom: D.zoom, hover: D.hover, empty: offText(D.data) });
256
260
  }
257
261
 
258
262
  /**
@@ -282,7 +286,7 @@ export function renderDepthInto(canvasId, h, { zoom = 0.025, compact = true } =
282
286
  ensureDepth(h);
283
287
  const canvas = document.getElementById(canvasId);
284
288
  if (!canvas) return null;
285
- return drawDepth(canvas, depthSeries(D.data), { zoom, compact });
289
+ return drawDepth(canvas, depthSeries(D.data), { zoom, compact, empty: offText(D.data) });
286
290
  }
287
291
 
288
292
  export function renderDepth(h) {