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
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,929 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses
|
|
5
|
+
[semantic versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] — 2026-09-16
|
|
10
|
+
|
|
11
|
+
The first minor release, two days after the initial 0.0.9, and everything since it in one place:
|
|
12
|
+
the 0.0.10 that was prepared on 2026-09-15 was never published, and its changes ship here. Four
|
|
13
|
+
things shaped it. The **first outside review** of 0.0.9 on bitcointalk, which called out a monitor
|
|
14
|
+
that bound every interface with no sign-in: 0.1.0 ships hardened — this machine only, sign-in on,
|
|
15
|
+
HTTPS with a certificate it makes itself, and no outbound connection at all until the exchange
|
|
16
|
+
feed is switched on. A day of **use on a laptop screen**, which found every tab a scroll: every
|
|
17
|
+
page now packs to one screen at 1093×945, and the Mining tab follows mempool.space's dashboard
|
|
18
|
+
with its **View more** panels. **Appearance**: the layout's colours are a setting — light or
|
|
19
|
+
dark, five themes, or nine colours of your own. And the **DOS Diversions**: Wolfenstein 3D, DOOM
|
|
20
|
+
and Quake, unmodified shareware on a PC emulated in the browser, written here with no
|
|
21
|
+
dependencies, because "can it run DOOM?" deserved a straight answer. 945 tests, from 862.
|
|
22
|
+
|
|
23
|
+
Upgrading from 0.0.9: [docs/INSTALL.md §11](docs/INSTALL.md#11-updating) — the first start after
|
|
24
|
+
the update serves HTTPS, creates the `admin` account and prints its password once.
|
|
25
|
+
|
|
26
|
+
### Security
|
|
27
|
+
|
|
28
|
+
- **Hardened defaults** (after the first outside review of 0.0.9): the monitor binds `127.0.0.1`
|
|
29
|
+
and requires sign-in out of the box — the first start creates the `admin` account and prints its
|
|
30
|
+
password once (or takes `BLOCKYARD_ADMIN_PASSWORD`). Reach it from elsewhere over an SSH tunnel,
|
|
31
|
+
or bind a LAN address with `BLOCKYARD_BIND` / `server.hosts`; open read-only access is a choice
|
|
32
|
+
(`BLOCKYARD_AUTH=0`), announced at boot. The boot banner says how to reach a loopback-only
|
|
33
|
+
monitor. Existing installs keep whatever `config/local.json` says.
|
|
34
|
+
- **HTTPS by default.** Every listener serves HTTPS; with no certificate named, the first start
|
|
35
|
+
makes the monitor its own self-signed one under `<data>/tls/` — written without dependencies
|
|
36
|
+
(`server/tls/selfsigned.js`: a v3 X.509 certificate assembled in DER and signed with ECDSA P-256
|
|
37
|
+
over SHA-256), naming the bound hosts, this machine's addresses and hostname — and remakes it
|
|
38
|
+
near expiry or when a bound address is missing from it. `blockyard tls` remakes it by hand
|
|
39
|
+
(`--san` to add names, `--print` to export it). Your own certificate still replaces it;
|
|
40
|
+
`BLOCKYARD_TLS=0` serves plain HTTP behind a proxy. The installer's summary and port probe know
|
|
41
|
+
both schemes.
|
|
42
|
+
- **The installer asks twice before a non-loopback bind.** A LAN or wildcard address is written
|
|
43
|
+
only when the operator types it and confirms it; the default it offers is `127.0.0.1`.
|
|
44
|
+
- **Market polling is off by default** — a new checkbox, **Display settings → Markets & Price →
|
|
45
|
+
Enable market polling**, shared by every screen. A fresh install makes no outbound connection
|
|
46
|
+
but to the node: the Markets and Kiosk tabs say that polling is off and where the switch is,
|
|
47
|
+
Overview's price line stays hidden and the explorer shows no dollar figures. Ticking it takes
|
|
48
|
+
effect at once; unticking parks the feed at once. `BLOCKYARD_MARKETS=0` still removes the feed
|
|
49
|
+
so that the checkbox cannot turn it on. Existing installs tick the box once.
|
|
50
|
+
|
|
51
|
+
### Appearance
|
|
52
|
+
|
|
53
|
+
- **A first tab in Display settings for the colours of the layout.** A theme mode — Light, Dark,
|
|
54
|
+
or System, which follows the operating system and changes with it — and theme cards with swatch
|
|
55
|
+
strips of the face the mode picks: **BlockYard** (the shipped look, the default; its dark face
|
|
56
|
+
is value for value what the monitor drew before), **Mono** (clean grayscale), **Nous** (GitHub's
|
|
57
|
+
chrome with a Nous blue accent), **GitHub** (Light Default and Dark Default) and **Catppuccin**
|
|
58
|
+
(Latte and Mocha). **Custom** is nine colour pickers — page, panels, text, muted text, accent,
|
|
59
|
+
lines, good, warning, bad — with the rest of a face derived from them (a raised panel, the
|
|
60
|
+
softer rules, the fainter text, the darker accent of a pressed button), and whether Custom is
|
|
61
|
+
light or dark read off its page colour; **customise <theme>** copies the theme on screen into
|
|
62
|
+
the pickers so a custom scheme starts from a look. Saved on the server with the other settings,
|
|
63
|
+
so every screen of the monitor draws the same; the sign-in page draws in the theme the browser
|
|
64
|
+
last saw.
|
|
65
|
+
- The theme is applied through the CSSOM (the page's custom properties rewritten on `<html>`),
|
|
66
|
+
which the content-security policy permits where a style attribute is refused. The charts' axis
|
|
67
|
+
text, grid lines and tooltips, the order-book depth chart's two sides and the market charts'
|
|
68
|
+
grounds follow the theme — they were literals of the dark look, and a light theme would have
|
|
69
|
+
drawn grey-on-grey. The 3D boards stay space, and the Explorer's block and transaction pages
|
|
70
|
+
keep their own dark cards.
|
|
71
|
+
|
|
72
|
+
### Mining
|
|
73
|
+
|
|
74
|
+
- **The Mining tab has the network row**: reward stats over the last 144 blocks, the difficulty
|
|
75
|
+
period (blocks remaining, the estimated and the previous change, the next halving), a week of
|
|
76
|
+
pools with luck and counts, a year of hashrate with the difficulty's steps, and the last twelve
|
|
77
|
+
adjustments as a table — every figure from this node alone (`server/collect/network.js`:
|
|
78
|
+
`getblockstats` for the reward window, block headers for the periods and a daily sample, the
|
|
79
|
+
week's coinbases read in the background at the lowest RPC priority).
|
|
80
|
+
- **mempool.space's layout**: two columns in its order (reward stats, difficulty adjustment;
|
|
81
|
+
pools, hashrate; recent blocks, adjustments) under the block flow, our own panels beneath, and
|
|
82
|
+
**View more »** on four cards opening that card full screen — the whole week of pools as a
|
|
83
|
+
table, the year's chart large, the whole attributed window of blocks, a year of adjustments.
|
|
84
|
+
- **The pool chart is a labelled pie**: every pool named beside its slice and joined to it by a
|
|
85
|
+
leader in the slice's colour, the small ones stacked, slices under half a percent gathered as
|
|
86
|
+
"Other" — and the "Other" threshold rises until every label fits the card, so no name runs off
|
|
87
|
+
the bottom at any window height. The pie and the year chart size themselves to the window.
|
|
88
|
+
- **Dollar figures** on the reward stats while market polling is on, from a new `/api/price` that
|
|
89
|
+
answers the cached spot price and never starts the exchange polling itself.
|
|
90
|
+
- The Mempool space viewer left this page — it is on Overview, Block space and Mempool — and with
|
|
91
|
+
it the full-pool poll the page used to ask for. The reward window is exactly 144 blocks.
|
|
92
|
+
- At a narrow window the block flow spans the width and is thinner, and the packages table scrolls
|
|
93
|
+
inside its card instead of bleeding into the neighbour; the three stacks become two below 1400px.
|
|
94
|
+
- **A streak of stale drops of the full-pool poll is one story**: one warning event when it
|
|
95
|
+
starts, a counter on Node & RPC while it lasts, one event when the poll answers again with the
|
|
96
|
+
count and the span. On a day the node answered slowly for thirteen hours, each drop had been its
|
|
97
|
+
own event, 188 of the feed's 200 rows.
|
|
98
|
+
|
|
99
|
+
### Every tab on one screen
|
|
100
|
+
|
|
101
|
+
Measured at 1093×945 and 1600×1000 with a dead-height probe, page by page:
|
|
102
|
+
|
|
103
|
+
- **Globally**: tighter card padding and heading spacing, and a short card beside a tall one keeps
|
|
104
|
+
its own height instead of being stretched to match (Chain & Sync's drill-down card had a
|
|
105
|
+
thousand pixels of nothing under its input; Node & RPC's poll cadence and connection cards
|
|
106
|
+
hundreds).
|
|
107
|
+
- **Overview**: the block flow card a quarter shorter, and the Block space board capped to the
|
|
108
|
+
viewport (it set the whole right column's height), so the events are on the first screen.
|
|
109
|
+
- **Block space**: the card tighter and the board sized so the whole page, note included, fits the
|
|
110
|
+
viewport without a scroll.
|
|
111
|
+
- **Markets**: the board takes what the window leaves, and the exchange table and the order-book
|
|
112
|
+
depth sit side by side beneath it (under 1400px the table drops the pair, 24 h range and volume
|
|
113
|
+
columns; all still in the summary line and on the Kiosk) instead of the depth chart starting a
|
|
114
|
+
thousand pixels down.
|
|
115
|
+
- **Kiosk**: tighter heads and gaps, the price panel's padding trimmed, the Block flow strip a
|
|
116
|
+
quarter shorter, and the full-screen button in that strip's head instead of floating over its
|
|
117
|
+
cards.
|
|
118
|
+
- **Mempool**: keeps its two columns down to 900px (it stacked from 1100), and the room over goes
|
|
119
|
+
to its time-series charts, a fifth taller.
|
|
120
|
+
- **Explorer**: a smaller title, the block strip's padding and scrollbar band trimmed, tighter
|
|
121
|
+
table rows and the table reaching the window's bottom edge — three more blocks on the first
|
|
122
|
+
screen.
|
|
123
|
+
- **Peers**: a long onion or i2p address no longer makes the whole page scroll sideways (it clips
|
|
124
|
+
with an ellipsis and carries the full text as its title; a wide table scrolls inside its card),
|
|
125
|
+
rows and service badges tighter, the top strip's cards at their own heights.
|
|
126
|
+
- **Network**: Throughput beside a stack of Upload and Traffic accounting, the throughput chart
|
|
127
|
+
taller, and the sources table taking the rest of the window.
|
|
128
|
+
- **Events**: the feed reaches the window's bottom edge (it stopped a hundred pixels short).
|
|
129
|
+
- **Chain & Sync**: one-line notes under the block-size and tip charts (the longer sentences are
|
|
130
|
+
their tooltips), the transaction-rate figures as one wrapping line, values that keep to their
|
|
131
|
+
line, and a shorter uptime label — the first three rows sit level and the drill-down and indexes
|
|
132
|
+
reach the first screen.
|
|
133
|
+
- **Node & RPC**: two stacks (RPC etiquette with its figures in two columns, Data quality and Node
|
|
134
|
+
connection; Poll cadence, self-telemetry beside the log tail, Panel sources and the RPC
|
|
135
|
+
console) instead of grid rows that left holes beside the short cards.
|
|
136
|
+
- **Admin**: two stacks (Users over the audit log; Node actions over Change own password, whose
|
|
137
|
+
two fields share a row). **Sign-in** and **404**: the same tighter card.
|
|
138
|
+
- **Display settings**: the tabs in three labelled rows — Boards (Appearance, Block space, Sky,
|
|
139
|
+
Markets & Price), Effects, and the Diversions last; rows, note and groups tighter, the sheet a little wider so hints wrap
|
|
140
|
+
less, and on the two effects tabs the switches run two across with each hint as the row's
|
|
141
|
+
tooltip — the Space effects tab is under half the scrolling it was. The price line on Overview
|
|
142
|
+
switch sits directly under the polling switch it depends on.
|
|
143
|
+
- **About**: the sky runs to the bottom edge with the credit line at its foot, and the GitHub
|
|
144
|
+
link and the tip pill share a row.
|
|
145
|
+
- **The Diversions pages take the window**: the wells of Tetrust, Blockout and Blockanoid grow to
|
|
146
|
+
the window's height (they stopped at 600px), and the DOS screen is always 4:3 and as large as
|
|
147
|
+
the window allows — at a 1093px window the 320×200 picture had been drawn squashed into a
|
|
148
|
+
761×720 box.
|
|
149
|
+
|
|
150
|
+
### DOS Diversions
|
|
151
|
+
|
|
152
|
+
- **DOOM.** The shareware `DOOM.EXE` v1.9 and `DOOM1.WAD` from `games/doom_dos/`, unmodified,
|
|
153
|
+
running on a PC emulated in the browser with no dependencies: an i386 interpreter
|
|
154
|
+
(`public/js/x86.js`), the DOS/4GW machine around it — DOS, DPMI, BIOS, PIC, timer, keyboard and
|
|
155
|
+
a planar VGA (`dospc.js`) — and a Sound Blaster Pro 2 with an OPL3 for the effects and the music
|
|
156
|
+
(`soundcard.js`), in a worker. Demos, menus, savegames and ENDOOM all work; 35 frames a second.
|
|
157
|
+
Mouse capture, WASD keys by default (the switch rebinds the running game at once), smoothing,
|
|
158
|
+
full screen; pauses when you look away; savegames and DOOM's config stay in the browser.
|
|
159
|
+
- **Wolfenstein 3D.** The shareware `WOLF3D.EXE` v1.4 and its `.WL1` files from
|
|
160
|
+
`games/wolf3d_dos/`, unmodified, on the same emulated PC — which now also runs a 16-bit real-mode
|
|
161
|
+
DOS program: real mode in the CPU (segments times sixteen, a wrapping SP, 16-bit string
|
|
162
|
+
instructions and interrupt frames), a plain MZ loader with its relocations, DOS's memory
|
|
163
|
+
allocator, the interrupt vector table, and VGA write mode 1. Menus, demos, AdLib music,
|
|
164
|
+
digitised sound effects, savegames and high scores kept in the browser, at its full 70 frames a
|
|
165
|
+
second. It sits before DOOM and Quake in the Diversions menu.
|
|
166
|
+
- **Quake.** The shareware `QUAKE.EXE` v1.06 and `PAK0.PAK` from `games/quake_dos/`, unmodified,
|
|
167
|
+
on the same emulated PC — which now also plays the go32 stub and CWSDPMI for a DJGPP program:
|
|
168
|
+
segment bases and 16-bit code in the CPU, a COFF loader, a directory tree, DOS's system file
|
|
169
|
+
table, the BIOS tick count. Demos, menus, savegames, sound effects through the Sound Blaster,
|
|
170
|
+
and the order screen on quit. W A S D and mouse look on the first start, Quake's own bindings
|
|
171
|
+
after. Quake starts with a smaller view (`viewsize 80`); **−** and **=** change it and Quake
|
|
172
|
+
keeps the choice.
|
|
173
|
+
- **The emulated PC is fast.** Each instruction is decoded once and cached rather than decoded
|
|
174
|
+
every time it runs (writes into cached code clear what they overlap, because both games patch
|
|
175
|
+
their own drawing code), the arithmetic and the x87 forms Quake runs most have handlers of their
|
|
176
|
+
own, and memory addresses are formed inline: Quake from 77 to about 141 million instructions a
|
|
177
|
+
second, its `timedemo demo1` from 29.7 fps to over 50; in the browser Quake plays at 40–50
|
|
178
|
+
frames a second.
|
|
179
|
+
- **A way out of full screen**: an **exit full screen** button in the top corner while full
|
|
180
|
+
screen, and holding **Esc** for a second (a tap of Esc stays the game's menu, which is what the
|
|
181
|
+
keyboard lock is for); leaving unlocks the keyboard and releases the mouse.
|
|
182
|
+
- The game files are served at `/games/<game>/<path>` (`server/http/games.js`); the shareware
|
|
183
|
+
packages live under `games/` (DOOM's moved from `doom_dos/` at the repository root).
|
|
184
|
+
|
|
185
|
+
### Markets and the effects
|
|
186
|
+
|
|
187
|
+
- **Black hole**, a Markets effect (modelled on NASA's visualisation, svs.gsfc.nasa.gov/14576): a
|
|
188
|
+
point of darkness opens and grows to a horizon over 26 s, the price line bends round it as
|
|
189
|
+
through a lens (the Einstein-radius push), the candles it reaches stretch and lean toward it and
|
|
190
|
+
shrink away into it, and an accretion disk seen nearly edge-on — a thin band white-yellow at its
|
|
191
|
+
inner edge through orange to a dark red rim, five hundred fibres spiralling inward sheared by
|
|
192
|
+
the Keplerian flow, its far side lensed into an arch over the shadow and a smaller one beneath,
|
|
193
|
+
a photon ring hugging the horizon, starlight bent into arcs. **It crosses the board**: it opens
|
|
194
|
+
a fifth of the way in on one side and runs a straight line through the chart to a fifth from the
|
|
195
|
+
other, from one height to another, so the line is seen bending round it as it passes; the
|
|
196
|
+
candles' orbits ride along at a quarter of the first cut's speed. It finishes its crossing before
|
|
197
|
+
it lets go, and lets go evenly, every candle gliding home and growing back smoothly. It hovered
|
|
198
|
+
over Block space for a day and its cubes never came home cleanly enough, so it is a Markets
|
|
199
|
+
effect only.
|
|
200
|
+
- **Supernova** (the solar flare, renamed and remade on NASA Goddard's animation,
|
|
201
|
+
svs.gsfc.nasa.gov/20413): the star is a glint — halo, hot core, diffraction spikes — growing and
|
|
202
|
+
blue-shifting as it goes; the opening explosion is a **white-hot flash**, held and washing far
|
|
203
|
+
out, that throws **a hundred and twenty-eight plumes of white gas** in every direction, each
|
|
204
|
+
flying and growing on its own out to nine star radii and drifting on until the effect is nearly
|
|
205
|
+
done (drawn on a half-resolution layer to keep the frame cheap); the debris is a volume of gas in
|
|
206
|
+
three layers — a blue cloud out in front, a white one behind it into which **violet grows patch
|
|
207
|
+
by patch from a tenth of the run**, and a deep violet heart — with a soft shock band throwing
|
|
208
|
+
and shaking the candles it crosses; and as the cloud dims a **pulsar** beats seven times a
|
|
209
|
+
second at the centre, two thin beams sweeping round, inside a nebula of the same gas that holds
|
|
210
|
+
its violet weight until four fifths of the run. 16 s (24 on Markets).
|
|
211
|
+
- **Fireworks are a display**, on both boards: every shell drawn — the rocket's arc, a white flash
|
|
212
|
+
and a shockwave ring at the burst, seven shell kinds by turn (peony, chrysanthemum, willow, ring,
|
|
213
|
+
crossette, strobe and more), sixty to ninety sparks each a curved trail, a lens flare at every
|
|
214
|
+
burst, glitter rain, and a nebula of smoke that drifts up and lingers; up to ten shells on
|
|
215
|
+
Markets over 14 s (five on Block space), each lighting the candles under it in its own colour.
|
|
216
|
+
**Off by default** on both boards, kept for special occasions.
|
|
217
|
+
- **X-ray**, on both boards: a front sweeps the board and everything behind it goes x-ray — bodies
|
|
218
|
+
dimmed to glass, every edge lit, a raster of thin lines across each face — then develops back.
|
|
219
|
+
- **Breathe** and **Light saber**, on the price line: three slow swells from the plain wire to
|
|
220
|
+
white heat and back; and the line igniting from its left end, blue, green, red or purple by the
|
|
221
|
+
run, humming with a breathing glow, spitting sparks, retracting to the hilt.
|
|
222
|
+
- **The energy pulse is heat, not blue, and its head is a crackling ball of plasma**; **the scan
|
|
223
|
+
line is a light curtain** standing on the board floor to the chart's top; **ball lightning is a
|
|
224
|
+
light source**, its arcs discharges built by midpoint displacement rather than zigzags, a quarter
|
|
225
|
+
of its size and a third of its speed on Markets; the scanner's cone is dimmer on both boards.
|
|
226
|
+
- **The Markets board keeps its price fit across refreshes**: the last fit is kept while the data
|
|
227
|
+
still sits inside it and fills at least two-thirds of it, so a refresh no longer re-scales the
|
|
228
|
+
whole chart unanimated. Whatever a head pulls is painted last, nearest the head on top; a held
|
|
229
|
+
pool refresh waits a moment and a half after an idle effect ends, with the board whole and still.
|
|
230
|
+
- **Layered soft fills add up correctly** (`softStops`): each nested disc carries only its
|
|
231
|
+
increment, so a stop's cumulative opacity is exactly the stop's; measured on the Kiosk, the
|
|
232
|
+
supernova's peak opacity fell from about 0.9 to about 0.45 and the chart shows through it.
|
|
233
|
+
- **The no-repeat window holds from the first pick** on both boards: every effect plays once
|
|
234
|
+
before any repeats (a negative slice index had let one come round at the tenth pick). The
|
|
235
|
+
searchlight and the tractor beam, which fly the same saucer, share the window.
|
|
236
|
+
- **A Kiosk test suite** (`test/kiosk.test.js`): every Markets and Block space effect plays through
|
|
237
|
+
the frame loop on a Kiosk-sized panel; the supernova, the black hole and the fireworks are
|
|
238
|
+
bounded by the board's width so no panel is covered.
|
|
239
|
+
|
|
240
|
+
### Fixed
|
|
241
|
+
|
|
242
|
+
- **The donation QR shows on Safari.** It was an inline SVG of 800 module rects, and Safari on
|
|
243
|
+
the first Mac install painted its white ground and none of the rects. It is a PNG now, painted
|
|
244
|
+
the same everywhere.
|
|
245
|
+
- **A dead index worker fails the build instead of hanging it.** A worker killed outright (out of
|
|
246
|
+
memory, with four beside the node) left its job unanswered and the build sat at "scan 5,720 of
|
|
247
|
+
5,721, about 1 s left" for an hour and a half on the first Mac install. The run now rejects the
|
|
248
|
+
moment a worker exits or throws, naming the job and the fix (fewer `addressIndexWorkers`); the
|
|
249
|
+
progress flag says *no progress for N min* when nothing has moved.
|
|
250
|
+
- The 0.0.9 release notes' test count corrected (862, not 864).
|
|
251
|
+
|
|
252
|
+
### Documentation
|
|
253
|
+
|
|
254
|
+
- INSTALL, GETTING-STARTED, SECURITY, CONFIGURATION, API, TROUBLESHOOTING and the README describe
|
|
255
|
+
the hardened defaults, HTTPS and the polling switch; INSTALL §11 has the notes for updating a
|
|
256
|
+
0.0.9 install; the USER-GUIDE has the Appearance section, the Mining row and the packed layouts;
|
|
257
|
+
TROUBLESHOOTING explains a streak of stale pool-poll drops. Screenshots re-shot at 0.1.0, with a
|
|
258
|
+
Mining shot added. The announcement for the bitcointalk thread is in `docs/announcement/0.1.0/`.
|
|
259
|
+
|
|
260
|
+
## [0.0.9] — 2026-09-14
|
|
261
|
+
|
|
262
|
+
The initial release. Everything in it, like everything before it, was written by an AI directed by a human
|
|
263
|
+
operator, and audited by AI (`docs/SECURITY-AUDIT.md`, `docs/SECURITY-AUDIT-2026-09-14.md`). It is
|
|
264
|
+
experimental pre-release software. BlockYard runs **on the machine that runs Bitcoin Core** (25.0 or
|
|
265
|
+
later), because the explorer's address index is built from the node's own block files; a node on
|
|
266
|
+
another machine, and the experimental node that earlier measurements were taken on, are not supported.
|
|
267
|
+
864 unit tests, no dependencies. Tagged `v0.0.9`. **The address index takes a few hours to build** on first start (about two on four workers on NVMe); every other page works meanwhile.
|
|
268
|
+
|
|
269
|
+
### The 3D boards' effects, per board
|
|
270
|
+
|
|
271
|
+
- **Each 3D board has its own effects list.** The one Effects tab is now two: **Space effects**
|
|
272
|
+
(the Block space board's 28 switches — every effect but the two drawn on a price line) and
|
|
273
|
+
**Market effects** (the Markets board's 12: ripple, outline sweep, tide, cascade, twinkle,
|
|
274
|
+
scan line, fireworks, solar flare, wave, energy pulse, pipe bulge, ball lightning — the ones
|
|
275
|
+
that translate to a candle chart; ball lightning is half its Block space size there). Each has its own no-repeat window and its own
|
|
276
|
+
all on / all off, which the no-repeat slider had silently taken away. A saved settings store is
|
|
277
|
+
migrated (schema 4): the Markets list starts as a copy of the list there was.
|
|
278
|
+
- **Each effects tab sets its board's cadence.** *Between effects, at least* / *at most*
|
|
279
|
+
(seconds, 5–9 by default, up to ten minutes on Space effects and five on Market effects) on both tabs, and *First effect after landing*
|
|
280
|
+
(1.2 s by default, up to two minutes) on Space effects only — the candle board has no landing
|
|
281
|
+
— drive the scheduler's own timers, per board.
|
|
282
|
+
The no-repeat slider on each tab tops out at that list's length.
|
|
283
|
+
- **Nothing on the Markets board waits its turn.** The pulse, the bulge and ball lightning were
|
|
284
|
+
held to a clock of 2.5-6 minutes between plays; they are ordinary picks now, and the list you
|
|
285
|
+
leave on decides how often any one of them comes round.
|
|
286
|
+
- **Everything on the Markets board moves along the hours** — left or right, never toward the
|
|
287
|
+
viewer — and lights the candles or the line: fronts (outline, scan, tide, wave) run along the
|
|
288
|
+
chart, rings (ripple, shockwave, nova) start on the candle row, the light cycles ride in from
|
|
289
|
+
the two ends with their walls on the candle tops, the lightning ball rides the price line, and
|
|
290
|
+
ball lightning flies through the chart's own height. Candle faces light under an effect now
|
|
291
|
+
(the camera is low, and a glow painted on a candle's top alone was invisible).
|
|
292
|
+
- **Ball lightning strikes candles.** `cellTops` began each tile at its own fractional x and y,
|
|
293
|
+
which on the candle board (tiles between grid lines) stored nothing, so every cell top was
|
|
294
|
+
zero and no arc found a target. Half the arcs now chain on from the struck block to
|
|
295
|
+
another in electric green, the instant they land, and half of those on to a third block.
|
|
296
|
+
- The lightning ball is one pale gradient with a white-hot heart, and a pale burn behind it,
|
|
297
|
+
instead of five stacked discs of deepening blue that read as a dark blot.
|
|
298
|
+
|
|
299
|
+
### The explorer's address history, from an index of our own
|
|
300
|
+
|
|
301
|
+
- **Address pages show full history and balances on Bitcoin Core.** This was the explorer's one
|
|
302
|
+
real gap, and it was not a bug: Core has **no address index at any setting**. `getaddressbalance`
|
|
303
|
+
and `getaddresstxids` are insight-style extensions carried by forks, and stock Core answers
|
|
304
|
+
`Method not found` (measured 2026-09-13 against two Core nodes). mempool.space
|
|
305
|
+
shows the same address's history only because `electrs` builds that index from the block files
|
|
306
|
+
itself. So this monitor now does the same, in a form it can afford: `server/chain/`.
|
|
307
|
+
- **Reading the node's own files, not asking the node.** `blk*.dat` on a current Core is
|
|
308
|
+
XOR-obfuscated at rest (`-blocksxor`, default since v28, the key in `blocks/xor.dat`) -- the
|
|
309
|
+
first spike concluded "not a Core datadir" and was wrong. De-obfuscated, `server/chain/blockfile.js`
|
|
310
|
+
frames the records; `server/chain/tx.js` decodes transactions and blocks, checked
|
|
311
|
+
**field-for-field** against `getblock <hash> 3` (91,813 spent coins in 16 sampled file pairs, 0
|
|
312
|
+
mismatches); and the undo (`rev*.dat`) records supply each spent coin's script and amount,
|
|
313
|
+
which is how the spending side is known without replaying the UTXO set. Pairing blocks with
|
|
314
|
+
undo records by trying every candidate was quadratic on the tiny early blocks (file 0: 35 minutes
|
|
315
|
+
and not finished); Core appends undo in connection order, so blocks are put in chain order by
|
|
316
|
+
their previous-block links and walked in step -- file 0 pairs in 5.9 s.
|
|
317
|
+
- **A lean row.** One 21-byte row per (script, transaction that touched it): 8 bytes of
|
|
318
|
+
sha256(script), the height, the position in the block, and the **net satoshis** the transaction
|
|
319
|
+
moved for that script -- so a balance is a sum, with no node call. A script paid and spent in the
|
|
320
|
+
same transaction is one row (18% fewer). The full decoder builds Core's verbose shape and was 5.9
|
|
321
|
+
of the 9.5 single-core hours measured for the chain; the index needs only each output's value and
|
|
322
|
+
script bytes, so `server/chain/index/rows.js` walks the raw transaction itself and is checked
|
|
323
|
+
row-for-row against rows from the full decoder.
|
|
324
|
+
- **Flat sorted files, no database.** Measured against `node:sqlite` on three real file pairs
|
|
325
|
+
(`docs/MEASUREMENTS.md` §29): 21 bytes per row against 24-27, a 5.5 M rows/s sort against a
|
|
326
|
+
1.4 M rows/s key-ordered load, and no B-tree collapse once the keys outgrow memory. 256 sorted
|
|
327
|
+
segments by key prefix with a sparse index (one key per 4,096 rows) held in memory; a lookup
|
|
328
|
+
binary-searches the sparse keys and reads the one 86 KB block that can hold its key.
|
|
329
|
+
- **Built and measured** (§30): `node scripts/index-build.js --out <dir> --workers 16` read all
|
|
330
|
+
5,756 file pairs in **29 min 45 s** (7.8 CPU-hours across 16 workers) and wrote
|
|
331
|
+
**5,890,519,289 rows, 123.7 GB** -- every height present exactly once, two stale blocks skipped,
|
|
332
|
+
and within 1.5% of the projection. **40 of 40 balances equal `scantxoutset`** at the same
|
|
333
|
+
height, to the satoshi; every (script, transaction) pair of four whole blocks from 2009 to the
|
|
334
|
+
tip found at its height and position with its amount. Lookups: **0.25 ms** median first touch,
|
|
335
|
+
0.03 ms warm; a 2.3 M-transaction address's whole history summed for its balance in 83 ms.
|
|
336
|
+
For scale: `scantxoutset`, the only thing Core offers, took 26.5 s for one scan of 40 addresses
|
|
337
|
+
holding the node's RPC thread, and answers only the current balance, never a history.
|
|
338
|
+
- **It follows the chain.** `server/chain/index/live.js`, started by the server for each configured
|
|
339
|
+
index directory, polls every 30 s, fetches each new block with `getblock <hash> 3` over RPC (so
|
|
340
|
+
the files are read once, at the build), and **logs the rows before serving them** in a
|
|
341
|
+
CRC-framed `live.log` that is replayed on restart and drops a record torn by a crash. A
|
|
342
|
+
reorganisation rolls the tail back to the fork; blocks 100 deep are **folded** into immutable
|
|
343
|
+
sorted layers and layers past 32 are merged; a reorganisation below what is folded stops the
|
|
344
|
+
follower and the page says to rebuild. Checked live: 12 blocks caught up in 6.5 s, 40 of 40
|
|
345
|
+
balances then equal to `scantxoutset`, and on release day the index reached a new block 16 s
|
|
346
|
+
after the node did. The rows it derives through RPC agree row-for-row with the rows built from
|
|
347
|
+
the files (`test/chain-index-live.test.js`).
|
|
348
|
+
- **The address page reads it.** `addressIndex` in a node's config names the directory (one index
|
|
349
|
+
serves every node on the same chain); the page shows the transaction count, balance, total
|
|
350
|
+
received and sent, and the transactions newest first with the **net change each made**, 25 a
|
|
351
|
+
page, deep pages costing no more than the first. The reply carries `index.tip`, `index.behind`,
|
|
352
|
+
`index.following` and `index.stale`, and the page says when the index is behind the node or has
|
|
353
|
+
stopped following. Checked live: every row of four pages, including page 4 of a 2.3
|
|
354
|
+
M-transaction address, matched the node's decoded transaction for txid, height and amount.
|
|
355
|
+
- **Without an index, the page is honest rather than empty.** A node's refusal used to become `[]`,
|
|
356
|
+
then `txCount: 0`, then "no transactions in this node's address index" -- a fabricated zero
|
|
357
|
+
indistinguishable from an unused address. The reply now carries `indexed: false` with a **null**
|
|
358
|
+
count, the page says the index is absent, and the dead RPCs are not re-sent on every view: a
|
|
359
|
+
"method not found" is remembered per node for ten minutes, then asked again, because the daemon
|
|
360
|
+
behind a node id can change.
|
|
361
|
+
- **A rebuild in place is safe.** `buildIndex` empties its output directory first, so a follower's
|
|
362
|
+
`live.log` and layers -- holding the reorganised-away blocks -- cannot survive into a new index and
|
|
363
|
+
a fresh follower catches up on the chain as it is (`test/chain-index-live.test.js`). Stop the
|
|
364
|
+
server, run the same command, start it; the advice to build into a fresh directory is withdrawn.
|
|
365
|
+
- **A missing address index is built by BlockYard itself, in the background** (operator: "Is it
|
|
366
|
+
possible to run step 6 in the background, and have a status notification in blockyard when the
|
|
367
|
+
index process is finished?"). On start, an `addressIndex` directory with no index in it is built
|
|
368
|
+
on worker threads while every page keeps serving; the progress is a quality flag on the Overview
|
|
369
|
+
and a line on the address page (phase, files done, rows so far, time left); an event marks the
|
|
370
|
+
start, the finish and a failure, and the browser toasts it; when it finishes the follower starts
|
|
371
|
+
and address pages work with no restart. `addressIndexBuild: "manual"` keeps it from happening.
|
|
372
|
+
The installer's step 6 offers background (the default), here, or later.
|
|
373
|
+
- **The build shares the machine with the node, and behaves like it.** Every build -- the server's
|
|
374
|
+
background one, the installer's build-here and `scripts/index-build.js` -- goes through one pacer
|
|
375
|
+
(`rpcPacer`) that reads the monitor's own RPC telemetry: before each file it holds, ten seconds at
|
|
376
|
+
a time, while the node's RPC is failing, its breaker is open or its answers average more than
|
|
377
|
+
`rpc.slowLatencyMs` (5 s), and eases to one file at a time with a pause between above 40% of that.
|
|
378
|
+
It first held at one second, which ran a healthy build on a node whose heavy reads take a second
|
|
379
|
+
at a sixth of its speed; the threshold is the monitor's own notion of slow now. The height batches
|
|
380
|
+
are paced the same way, 1,000 at a time at the lowest priority. The server's build opens its own
|
|
381
|
+
RPC connection, as `scripts/index-build.js` always did, so it and the monitor stop starving each
|
|
382
|
+
other: on the first Mac its `getblockhash` batches queued behind multi-second mempool and block
|
|
383
|
+
reads on the monitor's one lane, and both sides starved ("heights 1,000 of 967,015" for a quarter
|
|
384
|
+
of an hour). Workers: `addressIndexWorkers` in the node's config, written by the installer; unset,
|
|
385
|
+
the server takes half of what a dedicated build would and at most four. The `rpc-slow` and
|
|
386
|
+
`rpc-timeouts` quality flags name the build when one is running and no longer assert a cause from
|
|
387
|
+
another node's era. **An interrupted build starts over**: there is no resume after Ctrl-C, and the
|
|
388
|
+
installer says so when it happens.
|
|
389
|
+
- **An address's unspent outputs are listed** (operator, the same day: "Why don't we do this"): the
|
|
390
|
+
index names every transaction that touched the address, each one's outputs paying it are asked of
|
|
391
|
+
`gettxout` (the UTXO set, less what the mempool already spends), and the page lists them with the
|
|
392
|
+
index's own height and their count on the card. The walk is the whole history, so it is made for
|
|
393
|
+
an address with at most 100 transactions and declined in words for a longer one.
|
|
394
|
+
- **Not yet:** an address's transactions still in the mempool.
|
|
395
|
+
- **An unconfirmed transaction shows its inputs and fee** (operator: "Unknown script?!", of a
|
|
396
|
+
mempool transaction whose 858 inputs all read *unknown script*). Core carries no `prevout` on a
|
|
397
|
+
mempool transaction's inputs, so `fillPrevouts` fetches the parents in one batch and fills each
|
|
398
|
+
input's script and amount; checked on that transaction, 858 of 858 inputs, and a fee of 77,958
|
|
399
|
+
sat equal to `getmempoolentry`.
|
|
400
|
+
|
|
401
|
+
### The installer: `npm run setup`, `npm run check`, and the `blockyard` command
|
|
402
|
+
|
|
403
|
+
- **An installer: `npm run setup`** (operator: "build a test into the installer so we can verify it
|
|
404
|
+
properly connects to an RPC server and finds the bitcoin logs ... something that writes out a
|
|
405
|
+
config/local.json at the end ... that we can up and run immediately to start building the
|
|
406
|
+
transaction set"). It asks for the RPC URL and data directory, finds the cookie (or asks for
|
|
407
|
+
`rpcUser`/`rpcPassword`), and proves the answers before writing anything: the RPC server answers
|
|
408
|
+
and on which chain, the node's version is 25.0 or later, `txindex` is on and synced,
|
|
409
|
+
`getblock <tip> 3` carries prevouts, the node is not pruned, `blocks/` holds matched block and
|
|
410
|
+
undo files and the first one opens to the genesis block through the XOR key, `debug.log` is where
|
|
411
|
+
it should be, and a configured index directory is readable, writable and not far behind. Then it
|
|
412
|
+
writes `config/local.json` (mode 0600, a backup of any previous one) and offers to build the
|
|
413
|
+
address index on the spot. `npm run check` runs the same checks against every configured node
|
|
414
|
+
and exits non-zero on a FAIL; `--yes` with flags is the scripted form. `docs/GETTING-STARTED.md`
|
|
415
|
+
walks a macOS or Linux command prompt through it. Six numbered steps, every answer validated and
|
|
416
|
+
explained before it is accepted (a URL that is not one, a port off the range, a directory that
|
|
417
|
+
is not there, a hostname where the config wants an address), a warning when something already
|
|
418
|
+
listens on the port, a progress bar with the rows so far and the time left for each phase of the
|
|
419
|
+
build (the same bar in `scripts/index-build.js`), Ctrl-C that leaves everything as it was, and a
|
|
420
|
+
last question that starts the monitor in the same terminal.
|
|
421
|
+
It reads the node's own `bitcoin.conf` first (operator: "can't you look through the user's
|
|
422
|
+
.conf and find the rpc values?"): chain, `rpcport`, `rpcconnect`, `rpcuser`/`rpcpassword`,
|
|
423
|
+
`rpcauth` users, a cookie file named elsewhere, `server=`, `txindex=`, `prune=`, sections and
|
|
424
|
+
`includeconf=`, so the RPC URL and the credentials arrive as defaults rather than questions.
|
|
425
|
+
- **Every check is timed**, because how fast the node answers is half of what an install needs to
|
|
426
|
+
know, and the verbose mempool read -- the monitor's heaviest regular call, every 20 s -- is one of
|
|
427
|
+
the checks, with a warning when it is slow enough to lag the board. An unbuilt index is reported
|
|
428
|
+
as information, not a fault.
|
|
429
|
+
- **Defaults**: the index in the checkout's `data/index`, like everything else the install writes;
|
|
430
|
+
at most **four** build workers (the shared-machine number; one on spinning disks, each needing
|
|
431
|
+
~2.5 GB of memory); the build in the background once BlockYard starts.
|
|
432
|
+
- **The banner** (operator: "I want the installer to have amazing ANSI Art here for the BY logo",
|
|
433
|
+
then "It needs to fit in 80 character space. Standard CRT"): the BY monogram -- the same tile the
|
|
434
|
+
favicon is -- in 26x12 solid cells, one cell per pixel painted as a background colour so it fills
|
|
435
|
+
the cell in every font, the About text beside it, and the whole run inside 80 columns (piped at
|
|
436
|
+
80: widest line 79). Every line wraps to the terminal with its indent kept, a box cuts a line past
|
|
437
|
+
its room with an ellipsis, widths are measured on what is seen rather than on colour codes, and
|
|
438
|
+
colour steps aside without a TTY or under `NO_COLOR` (`FORCE_COLOR` turns it on).
|
|
439
|
+
- **An npm package and a `blockyard` command** (operator: "push to npm"). `package.json` loses
|
|
440
|
+
`private` and gains `bin` and `files` (bin, server, public, scripts, the pool map, systemd, the
|
|
441
|
+
docs, the licence and notice; no tests, no images: 924 kB packed). `blockyard setup | start |
|
|
442
|
+
check | index-build | users` keeps the config and data under `~/.blockyard` (`BLOCKYARD_HOME`),
|
|
443
|
+
since a global install's own directory is nowhere to keep a config or 124 GB of index; a checkout
|
|
444
|
+
run with `npm run …` is unchanged.
|
|
445
|
+
|
|
446
|
+
### Fixes from the first fresh install (a Mac, Core 29.1)
|
|
447
|
+
|
|
448
|
+
- **A node without `coinstatsindex` was sent a full UTXO-set walk every minute.** Found on the
|
|
449
|
+
first Mac install: `getindexinfo` said there was no coinstats index, and the rule read that as
|
|
450
|
+
"not assumed unindexed" and asked `gettxoutsetinfo` anyway, on the slow tier, every 60 s -- a
|
|
451
|
+
walk of 165 M outputs that Core kept computing after the 90 s timeout, holding its chain lock,
|
|
452
|
+
so every other call answered in 18 s, the mempool read was dropped and the block-space board
|
|
453
|
+
stayed empty. Everything was blamed on the index build, which had nothing to do with it. The
|
|
454
|
+
UTXO figures are now asked only of a node that has said it keeps the index; the indexes are
|
|
455
|
+
asked first, alone, before anything expensive.
|
|
456
|
+
- **A fresh install attributed no blocks** and showed raw coinbase tags: the curated pool map
|
|
457
|
+
lived only in `data/`, written by a script nobody had run. The map (mempool.space's
|
|
458
|
+
mining-pools list, MIT, 151 pools) ships in `config/` and is used until `scripts/pool-map.js`
|
|
459
|
+
writes a newer one into `data/`.
|
|
460
|
+
- **Nothing holds hundreds of files open any more.** The index store kept one descriptor per
|
|
461
|
+
segment and layer -- 256 and more -- for the life of the process, and the build kept all 256
|
|
462
|
+
bucket files open through the scan; a stock macOS allows a process 256 (`ulimit -n`) before it
|
|
463
|
+
has opened a socket. A lookup opens the one file it reads and closes it (measured on the full
|
|
464
|
+
index afterwards: 0.02 ms median warm, 21 descriptors held by the whole process), and the build
|
|
465
|
+
keeps at most 64 bucket files open, least recently written closed first. Found while preparing
|
|
466
|
+
the first macOS install.
|
|
467
|
+
- **The installer wrote 16 workers when Enter was pressed.** The suggestion was the machine's core
|
|
468
|
+
count; it is the shared-machine number now, at most four. And it asked for what the node's own
|
|
469
|
+
`bitcoin.conf` already answered -- read first now, above.
|
|
470
|
+
- **The banner's half-block art seamed on the Mac's Terminal** (operator: "What is this garbage?!"):
|
|
471
|
+
a font decides where a half-block glyph sits in its cell. One character cell per pixel, painted as
|
|
472
|
+
a background colour, which every font fills.
|
|
473
|
+
- **Lines wider than 80 columns.** The summary box's one long line broke its frame on an 80-column
|
|
474
|
+
terminal; every line the installer says now wraps to the terminal width.
|
|
475
|
+
- **Block cards clipped and wrapped numbers on a wider font**: a value could wrap mid-number ("604"
|
|
476
|
+
drawn as "60" over "4"), and the stats columns clipped. Values keep their line, the columns size
|
|
477
|
+
to their content, and the card is ten pixels wider.
|
|
478
|
+
- **Windows.** The run-as-main check compared a file URL (`/C:/x`) with a realpath (`C:\x`) as
|
|
479
|
+
strings, so no script's `main()` ran; they are compared as paths (`fileURLToPath`). LF on every
|
|
480
|
+
checkout (`.gitattributes`): several tests match line-anchored patterns in source files, and a
|
|
481
|
+
CRLF checkout turned every one into a miss. The temp directory is the platform's, not `/tmp`;
|
|
482
|
+
defaults and paths are spelled per platform; a file-mode assertion knows Windows has none; the
|
|
483
|
+
scanner is keyed with `/`. **CI runs on Ubuntu, macOS and Windows**, Node 22 and 24, and the suite
|
|
484
|
+
passed on all six at `2010b24`.
|
|
485
|
+
|
|
486
|
+
### Sync: a long gap is not a stall
|
|
487
|
+
|
|
488
|
+
- **Stalled only when the peers know a higher tip.** Two independent nodes at the same height, no
|
|
489
|
+
block for 42 minutes, and the header said STALLED in red. The network finds no block for 40
|
|
490
|
+
minutes about once in fifty. The state is stalled only when a connected peer reports a tip above
|
|
491
|
+
this node's (`getpeerinfo` `synced_headers`, `startingheight` as the fallback); when the peers
|
|
492
|
+
agree on the tip the node is synced and the caveat names the gap as the network's; with no peer
|
|
493
|
+
height to check, the word waits for two hours. The caveats say which of the three it is.
|
|
494
|
+
- The sync detail's three log-derived rows exist only where the figure does; Bitcoin Core prints
|
|
495
|
+
none of them.
|
|
496
|
+
|
|
497
|
+
### Block space and Markets
|
|
498
|
+
|
|
499
|
+
- **Agent effects.** The board's idle repertoire is **30 effects**, each with a switch: to the
|
|
500
|
+
fields (ripples, plasma, code rain, fireworks and the rest) the operator asked for things that
|
|
501
|
+
*happen* -- "think more TRON light cycles" -- and fifty video-game-inspired effects were designed
|
|
502
|
+
(`docs/EFFECTS-AGENTS.md`), built, watched, and cut to the ones that earned their place. The
|
|
503
|
+
agents that stayed: **light cycles**, the **lightning ball**, a **centipede** that weaves down the
|
|
504
|
+
board and splits, a **UFO** whose tractor beam lifts the tallest transaction and drops it back
|
|
505
|
+
under gravity, **Missile Command** arcs against rising interceptors, **Boulder Dash** where the
|
|
506
|
+
board gives way from a point, and **ball lightning** drifting across the whole view, its arcs
|
|
507
|
+
electrifying the blocks they strike (it replaced a portal pair). Every agent is checked on a
|
|
508
|
+
flat uniform board too, so none can be blinded by a skyline it happens not to read.
|
|
509
|
+
- **No repeats within N** (default 12): an effect is never played again until that many others
|
|
510
|
+
have played; where fewer are switched on, the one that has waited longest plays next.
|
|
511
|
+
- **The Markets price line is one continuous pipe** rather than forty segments, with a rarer
|
|
512
|
+
**energy pulse** and a new **pipe bulge**, each of them rare -- 2.5-6 minutes between plays: a ball forced through the tube,
|
|
513
|
+
swelling the wall with an arced, stretched skin, the bright core magnified through it as through
|
|
514
|
+
a fish-eye lens; it enters at the line's first point at exactly the tube's size, is as large as
|
|
515
|
+
fits for as long as possible, leaves at the last, and runs quicker downhill than up.
|
|
516
|
+
- **A chrome finish** (Display settings → Metallic finish): every face mirrors a horizon that
|
|
517
|
+
slides as the blocks move; satin is the softer highlight that was there before.
|
|
518
|
+
- **Depth**: a touch of perspective, off by default, so a cube's top grows a little wider than its
|
|
519
|
+
base and a flying block swells as it rises. Capped at 0.001 after larger values put flyers
|
|
520
|
+
through resting cubes.
|
|
521
|
+
- **Departures and arrivals**: how blocks leave and rejoin the board on a refresh, chosen by
|
|
522
|
+
measurement (flights clear their neighbours before fanning, leave the frame rather than popping
|
|
523
|
+
at its edge, and take a lane per leg); a recoloured cube blends to its new colour instead of
|
|
524
|
+
popping; and where a flyer is clearly above a resting cube it paints over it.
|
|
525
|
+
- **The block-flow cards are linked as a chain that reads as one** (operator: "looks bad on a black
|
|
526
|
+
background. Re-do it to be much more stylized and visible"): two pale outlined pills that vanished
|
|
527
|
+
on the dark panel are two interlocked links now, accent-coloured tubes with a highlight and a
|
|
528
|
+
glow, the left link's top strand painted again over the right so the pair weaves.
|
|
529
|
+
|
|
530
|
+
### Security
|
|
531
|
+
|
|
532
|
+
- **The second AI security audit's findings, the same day** (`docs/SECURITY-AUDIT-2026-09-14.md`,
|
|
533
|
+
which re-verified the 09-13 fixes live and covered the address index and explorer). Its one
|
|
534
|
+
medium: `/api/x/address` sized an allocation by the request's page number, 525 MB for
|
|
535
|
+
`page=999999` on a two-row address -- a deep page now counts the history first and keeps at most
|
|
536
|
+
what exists. The rest: rows above the node's tip (a reorganised tail the follower has not yet
|
|
537
|
+
rolled back) are left out of the count, balance and page and reported as `index.postTip` with a
|
|
538
|
+
caveat on the page; a pool key is escaped like everything around it; a transaction summary with
|
|
539
|
+
more than 2,000 inputs and outputs is never cached; a test fixture no longer lives at a fixed
|
|
540
|
+
name in `/tmp`; `audit.jsonl` and `history.json` are created owner-only.
|
|
541
|
+
- **The first AI security audit** (`docs/SECURITY-AUDIT.md`, 2026-09-13) found one HIGH and four
|
|
542
|
+
more, all fixed the same day; they are the next four items.
|
|
543
|
+
- **The node-connection probe leaked the node's RPC credential** (HIGH, found by audit and
|
|
544
|
+
reproduced with a working exploit). `POST /api/config/node/test` built its throwaway client from
|
|
545
|
+
the live node's config, so `resolveCookie` read the real `.cookie` and sent it as an
|
|
546
|
+
`Authorization` header **to whatever URL the request named** -- on a request needing no session
|
|
547
|
+
and no CSRF token, reachable by a plain cross-site form. Credentials now go only to the endpoint
|
|
548
|
+
the monitor is already configured for; anywhere else is probed unauthenticated and says so. Open
|
|
549
|
+
mode additionally refuses any state-changing request whose `Origin` is not this server or whose
|
|
550
|
+
`Sec-Fetch-Site` says cross-site.
|
|
551
|
+
- **Session TTLs were inverted**: an 8-hour absolute lifetime with a 72-hour idle ceiling meant the
|
|
552
|
+
idle check could never fire. Now 72 h absolute, 8 h idle, with a test on the invariant.
|
|
553
|
+
- **`randomPassword()` drew with modulo bias**, over-representing the first 58 characters of its
|
|
554
|
+
66-character alphabet. It uses `crypto.randomInt` now.
|
|
555
|
+
- **The audit trail redacts by key shape**, not by two hardcoded field names, so an action echoing a
|
|
556
|
+
key-shaped argument cannot write a secret into the one file designed to be kept.
|
|
557
|
+
- A re-audit is planned after the first install (`AGENTS.md`).
|
|
558
|
+
|
|
559
|
+
### Scope, documentation and the name
|
|
560
|
+
|
|
561
|
+
- **A node on another machine, over RPC alone: tried, and dropped.** On 2026-09-13 the monitor was
|
|
562
|
+
pointed at a node appliance on the LAN with `rpcUser` / `rpcPassword` and no `datadir` -- a
|
|
563
|
+
configuration the validator had refused at boot until it was fixed -- and the monitor half
|
|
564
|
+
worked: chain, mempool, peers, blocks, the block being built. The explorer did not, and no
|
|
565
|
+
setting would make it: Core has no address index, `scantxoutset` holds the node's single RPC
|
|
566
|
+
thread for tens of seconds per query and knows no history, and a node answering over the
|
|
567
|
+
network in seconds left address pages waiting minutes. **Real-time explorer data over RPC was
|
|
568
|
+
a failed idea.** The address data is rebuilt from the block files and stored locally instead,
|
|
569
|
+
the way mempool.space's `electrs` does it (above), which is why BlockYard runs on the node's
|
|
570
|
+
machine. The install guide's appliance section is gone; `rpcUser` / `rpcPassword` remain for a
|
|
571
|
+
node that uses `rpcauth` instead of the cookie file, and the `bitcoin.conf` lines that measured
|
|
572
|
+
as worth having (`txindex`, `coinstatsindex`, `dbcache`) are kept, each annotated with what it
|
|
573
|
+
does for this monitor.
|
|
574
|
+
- **This is a Bitcoin Core-centric release.** The README, install, configuration, API and
|
|
575
|
+
architecture documents describe Core; the shipped defaults are Core's own (`id: main`,
|
|
576
|
+
`127.0.0.1:8332`, `~/.bitcoin`, `bitcoind.service`). Measurement records taken against a
|
|
577
|
+
non-Core build are anonymised rather than relabelled -- they describe what was measured, and
|
|
578
|
+
claiming otherwise would invent measurements that never happened.
|
|
579
|
+
- **Log parsing is documented as unavailable for Bitcoin Core.** The parsers target an experimental
|
|
580
|
+
node's log grammar; fed Core's `debug.log` they extract no figures and timestamp entries at read
|
|
581
|
+
time. The log source is off by default and should stay off against Core --
|
|
582
|
+
`test/log-core-unsupported.test.js` pins that so it cannot be assumed away.
|
|
583
|
+
- **The old name is gone from the tree** (operator: "WHY THE FUCK DO I STILL SEE THE OLD NAME
|
|
584
|
+
REFERENCES IN OUR TREE DOCS"): a `git grep` for the old prefix finds nothing but bytes in a JPEG.
|
|
585
|
+
The allowlist's vendor-prefixed read verbs -- an earlier node's, and that node is not supported --
|
|
586
|
+
are gone with their test, and a vendor-shaped method is denied by default; two scripts that
|
|
587
|
+
defaulted a CA path to a directory named after the old project take it from the environment or
|
|
588
|
+
nothing; `NOTICE` and `LICENSE` name BlockYard and its copyright holder.
|
|
589
|
+
- **Every document read against the code**: sessions are 72 h absolute / 8 h idle (two documents had
|
|
590
|
+
it inverted); display settings live on the server; the effects list is the thirty that exist; four
|
|
591
|
+
API routes that were undocumented are documented from their handlers; ARCHITECTURE gains the
|
|
592
|
+
address-index subsystem; three stale code comments and the mempool feed's stated cadence
|
|
593
|
+
(60 -> 20 s) corrected; screenshots re-shot against the local Core node. `docs/GETTING-STARTED.md`
|
|
594
|
+
walks a macOS or Linux command prompt through the install (brew's plain node formula, where
|
|
595
|
+
`bitcoin-cli` lives inside the macOS app bundle, one worker on spinning disks, the build's memory
|
|
596
|
+
per worker). `docs/DEFECTS.md` states the scope and closes the entries whose only subject was the
|
|
597
|
+
experimental node or a remote one. Eight one-off pixel probes whose questions are answered in
|
|
598
|
+
MEASUREMENTS and DEFECTS are removed; the checks still used stay.
|
|
599
|
+
- **Donations are in two places only**: the foot of the README, in small type, and the About page,
|
|
600
|
+
where the address is a pill that copies on a click (verified in a headless browser by reading the
|
|
601
|
+
clipboard back) beside a QR of it (`scripts/donate-qr.py` generates the inline SVG and checks it
|
|
602
|
+
decodes; the rendered page's screenshot decodes to the address too). They are out of the installer.
|
|
603
|
+
- **0.0.9 is the initial release number**, the operator's. The two earlier CHANGELOG sections that
|
|
604
|
+
carried version numbers were never tagged or released and are kept as dated milestones;
|
|
605
|
+
`server/main.js` reads the version from `package.json`, and `test/version.test.js` ties the
|
|
606
|
+
CHANGELOG's newest release and the README to it.
|
|
607
|
+
|
|
608
|
+
### Since the 2026-09-11 milestone: added
|
|
609
|
+
|
|
610
|
+
- **A node connection form**, on **Node & RPC** (operator: "Still left to do is a config connection
|
|
611
|
+
in the web settings. We have no way for users to configure a connection to their rpc backend").
|
|
612
|
+
Enter an RPC URL, a data directory, a chain and a label; **test connection** probes it with a
|
|
613
|
+
throwaway client that never touches the live node's request lane, and **save** is disabled until
|
|
614
|
+
a test has actually answered — and goes back to disabled the moment a field changes. No password
|
|
615
|
+
field: authentication is the node's own `.cookie`, found from the data directory.
|
|
616
|
+
- The save is honest about two things it would otherwise hide. It **keeps the fields the form does
|
|
617
|
+
not show** — `logFile`, `systemdUnit`, the colour — because the config merge replaces arrays
|
|
618
|
+
whole, so a naive write would quietly unconfigure the log tail. And where the environment sets
|
|
619
|
+
`BLOCKYARD_NODE_URL` (a systemd drop-in, say), it says the environment beats the file rather than
|
|
620
|
+
reporting a success the next restart contradicts.
|
|
621
|
+
- **The mempool page carries two panels it was already collecting data for.** `renderMempool` has
|
|
622
|
+
been writing ingest/reject figures and orphan-pool figures into elements that did not exist —
|
|
623
|
+
collected from the node's log, sent to the browser and dropped. They have cards now, and on a
|
|
624
|
+
monitor running without a log tail they say so rather than showing a column of dashes. The page
|
|
625
|
+
also lost four dead grid columns, and Pool usage gained the pool's total vsize, average vsize and
|
|
626
|
+
total fees, all of which were computed on every sample and never drawn.
|
|
627
|
+
- **The grid is yours, per board** (operator: "we need to break out the green grid settings per
|
|
628
|
+
game. We should also add a grid color picker, and a transparency slider ... I really want to turn
|
|
629
|
+
down the intensity on blockanoid", and "add a color selector and brightness setting for the grid
|
|
630
|
+
lighting for blockspace"). Block space, Tetrust, Blockout and Blockanoid each get a **grid
|
|
631
|
+
colour** and a **grid intensity**, and the three games get a **grid** switch as well; Block space
|
|
632
|
+
already had one. Every board is independent, so a court can be turned right down while the board
|
|
633
|
+
stays bright. Until now the colour was hardcoded green in four separate files and could not be
|
|
634
|
+
changed at all.
|
|
635
|
+
- One colour drives the whole grid rather than a single value. The board does not draw its grid in
|
|
636
|
+
one colour: it lays an opaque core under a translucent halo and glow with a brighter line along
|
|
637
|
+
the edge, and that relationship is deliberate — a see-through core reads dimmer wherever the
|
|
638
|
+
floor beneath it is shadowed, and composite modes are off the table. The picker recolours the
|
|
639
|
+
family and keeps each layer's relative weight; intensity multiplies them together.
|
|
640
|
+
|
|
641
|
+
- **Tetrust**: a playable Tetris on the 3D engine (`public/js/tetris.js` for the rules,
|
|
642
|
+
`public/js/tetrust.js` for the screen). The well is the block-space board with its oblique
|
|
643
|
+
camera and curved surface; a neon-blue wireframe marks where the piece will land; cleared
|
|
644
|
+
lines fly up off the top of the canvas. Seven-bag piece order, wall kicks, the classic score
|
|
645
|
+
table times the level, ten lines a level, and a top-ten high score table kept per browser.
|
|
646
|
+
Pauses when the tab or the page loses focus. Arrows or WASD.
|
|
647
|
+
- **Music and sound effects for Tetrust**, synthesised in the browser with the Web Audio API —
|
|
648
|
+
no audio files and no dependencies. Korobeiniki on a lookahead scheduler running on the audio
|
|
649
|
+
clock, and nine shaped tones for move, rotate, soft drop, hard drop, lock, line clear, tetris,
|
|
650
|
+
level up and game over. A switch for each.
|
|
651
|
+
- **Blockout**: Breakout on the 3D engine, in its own tab beside Tetrust (`public/js/breakout.js`
|
|
652
|
+
for the rules, `public/js/blockout.js` for the screen). The bat follows the mouse — or the arrow
|
|
653
|
+
keys — the wall is six rows of block-space stones one grid cell each, and where the ball lands on
|
|
654
|
+
the bat decides where it goes. Three balls, a faster wall each level, and a per-browser high score
|
|
655
|
+
table. The rules file has no DOM, no clock and no randomness (a launch angle is an argument), so
|
|
656
|
+
the whole of it runs under the test suite, and the ball is sub-stepped so it cannot tunnel through
|
|
657
|
+
a brick on a slow frame.
|
|
658
|
+
- **Every timed power expires after 30 seconds** — laser, wide, catch and slow, each counted down on
|
|
659
|
+
the heads-up display. Three balls and the extra life are one-shot and have nothing to run out. One
|
|
660
|
+
timer table and one expiry loop rather than four hand-written countdowns, for the same reason
|
|
661
|
+
`loseLife()` exists: separate copies of a rule drift apart. Slow puts the pace back when it lapses
|
|
662
|
+
(or "slow" would be permanent by omission) and wide restores the bat about its own centre,
|
|
663
|
+
re-gripping a held ball into the narrower span.
|
|
664
|
+
- **A caught ball is locked to the bat.** It recorded no grip, so a stuck ball held its absolute
|
|
665
|
+
position while the bat slid underneath and moved only when an edge caught up with it — which read
|
|
666
|
+
as the ball drifting around. It remembers where along the bat it landed and is placed from that.
|
|
667
|
+
- **The ball bounces off a minion** instead of passing through. The collision forced the ball
|
|
668
|
+
downward whatever direction it had arrived from, so dropping onto a minion pushed it further down.
|
|
669
|
+
It reflects on the axis of least penetration now, exactly as a brick does, which gives the side
|
|
670
|
+
bounces as well.
|
|
671
|
+
- **Catch expires after 30 seconds.** Held indefinitely it stopped being a power-up and became a
|
|
672
|
+
different game: park the ball, aim every shot, and the rally ceases to exist. The countdown runs
|
|
673
|
+
off `step`'s own elapsed milliseconds, like the laser cooldown and the minion timer, so the rules
|
|
674
|
+
still carry no clock and a run stays reproducible. A ball still held when it lapses is released at
|
|
675
|
+
the angle its position on the bat implies, rather than stranded there with nothing to explain it.
|
|
676
|
+
- **Minions, pill capsules and a bat that morphs.** The engine gained a rotated-polygon tile kind
|
|
677
|
+
(`poly` + `rot`, with optional `eyes`), built the way the sphere is — nested filled polygons,
|
|
678
|
+
since the op format has no arcs and gradients are forbidden — and claiming its own face name so
|
|
679
|
+
the sphere's "nothing but ball ops" guarantee is untouched. On it: four **minion** types with
|
|
680
|
+
their own silhouettes, spin rates and drift behaviours (a swinging cone, a tumbling cube, a
|
|
681
|
+
wobbling orb, a zig-zagging molecule), each with eyes; **capsules** are now elongated pills that
|
|
682
|
+
tumble as they fall, the angle taken from their own height rather than a clock so a frame stays
|
|
683
|
+
reproducible; and **Vaus grows cannons** and a raised housing while the laser is up, so the bat
|
|
684
|
+
shows what it can do rather than only changing colour. The capsules keep their per-capsule band
|
|
685
|
+
mark, so the seven remain separable without colour even though they now share one silhouette.
|
|
686
|
+
- **An About page**, reached by clicking the BlockYard monogram in the header rather than by a nav
|
|
687
|
+
tab of its own — the bar is already full enough to wrap below 2000 px. It shows the version and
|
|
688
|
+
live build, the host's operating system, architecture, processors, memory and runtime, and the
|
|
689
|
+
Bitcoin node's own version and protocol, over the spiral galaxy the other boards draw. New
|
|
690
|
+
`GET /api/about` supplies the host facts and deliberately reports no hostname, username, network
|
|
691
|
+
address or environment: the monitor is open-access by default, so the OS and processor describe
|
|
692
|
+
the machine's shape and never its owner.
|
|
693
|
+
- **A stylised BY monogram and a real gear.** The brand mark was the letter `B` in a tile; it is now
|
|
694
|
+
a drawn monogram whose tile, gradient and courses of blocks live in the SVG, so the favicon is the
|
|
695
|
+
same drawing. The settings button was a circle with eight radiating rays — the standard sun glyph,
|
|
696
|
+
which is why it read as a light/dark toggle — and is now a cog with teeth and a punched bore.
|
|
697
|
+
- **The Kiosk's price panel becomes Price & order book depth.** The 24 h high, low, volume,
|
|
698
|
+
spread across books and the per-exchange table are gone; in their place is the depth chart,
|
|
699
|
+
compact and toolbar-less, fixed at ±2.5% around the mid. A wall display is read from across a
|
|
700
|
+
room, where a four-column table is unreadable and the shape of the book says more than a spread
|
|
701
|
+
figure — all of it is still on the Markets tab. The chart shares the Markets tab's single poll,
|
|
702
|
+
so having both open does not double the traffic to five exchanges, and the depth endpoint marks
|
|
703
|
+
the collector as watched, so an unattended kiosk keeps its books fresh by itself.
|
|
704
|
+
- **Blockanoid**: Arkanoid on the 3D engine, the third Diversion (`public/js/arkanoid.js` for the
|
|
705
|
+
rules, `public/js/blockanoid.js` for the screen). Six hand-built walls that cycle; **silver**
|
|
706
|
+
bricks that take two hits and one more every four levels, standing lower once damaged; **gold**
|
|
707
|
+
that never breaks and never blocks a level, since a wall is cleared when its *breakable* bricks
|
|
708
|
+
are gone. Six **capsules** fall out of broken bricks — laser, enlarge, catch, slow, disrupt
|
|
709
|
+
(three balls) and player (a life) — one on the court at a time, as the
|
|
710
|
+
arcade did it. Vaus turns red while the laser is up, so the bat says what it can do. Minions
|
|
711
|
+
drift down the court and pay when destroyed. Which brick carries a capsule is a **hash of the
|
|
712
|
+
brick and the level, never `Math.random`**, so a wall always drops the same letters and the
|
|
713
|
+
whole thing is assertable under `node:test`. Capsules and minions each have a switch, and
|
|
714
|
+
because they change the rules rather than the look, flipping one reaches the game in play.
|
|
715
|
+
- **Seventeen new idle effects**, bringing the total then to **26** (30 at release; above), each with its own switch:
|
|
716
|
+
shockwave, nova, fireworks, solar flare, wave, quake, code rain, sparkle, checkerboard, radar,
|
|
717
|
+
vortex, laser, power-up, combo chain, aurora, plasma and glitch. All are pure functions of the
|
|
718
|
+
tile and the effect's clock, so each replays identically and is covered by tests rather than
|
|
719
|
+
by watching.
|
|
720
|
+
- **Block finishes**: **neon blocks** (a dim solid body in the block's fee-rate colour under lit
|
|
721
|
+
tubes on every visible edge) and a **metallic sheen** (a specular highlight on the lit edge of
|
|
722
|
+
each top face, a dark roll-off on the far one). Both work at every level of detail. The neon
|
|
723
|
+
tubes can take the block's own colour or one colour of your choosing, at a brightness you set.
|
|
724
|
+
- **A movable lamp**: `Light` chooses straight above (now the default for Block space), upper
|
|
725
|
+
left, upper right, or from the viewer.
|
|
726
|
+
- **A thickness slider for Tetrust's landing marker**, 0.3 to 2.5 times the shipped weight, so the
|
|
727
|
+
outline can be thinned out of the way of the stack behind it.
|
|
728
|
+
- Broken bricks in Blockout **fly up off the court** instead of vanishing, the same launch Tetrust's
|
|
729
|
+
cleared lines take.
|
|
730
|
+
- **`txindex=1` is documented as required** for the explorer's transaction pages — a transaction page
|
|
731
|
+
asks for `getrawtransaction <txid> 2` with no block hash, which a node without the index can only
|
|
732
|
+
answer for its mempool. Block pages pass the hash and are unaffected. Install, README and
|
|
733
|
+
troubleshooting all say so now.
|
|
734
|
+
- **The Tetrust landing marker's colour is a setting.** The wireframe showing where the falling
|
|
735
|
+
piece will land is drawn instead of a block rather than over one, so the neon finish never
|
|
736
|
+
applied to it and it stayed the shipped blue whatever else was changed; it has its own colour
|
|
737
|
+
now, and `tiles()` takes it as an argument so the rules file still knows nothing of the store.
|
|
738
|
+
- **A tabbed Display settings panel**, with all-on / all-off on the Effects tab, whose
|
|
739
|
+
twenty-six switches -- thirty now -- are a lot of clicking otherwise.
|
|
740
|
+
- **Markets remembers its toolbar**: the exchange and the range are settings now, so the page
|
|
741
|
+
opens where you left it.
|
|
742
|
+
|
|
743
|
+
### Since the 2026-09-11 milestone: changed
|
|
744
|
+
|
|
745
|
+
- **The block being built is assembled here now, and costs your node nothing.** It used to be a
|
|
746
|
+
`getblocktemplate` call worth 1.3-1.5 s of the node's single RPC thread and 1.79 MB per reply,
|
|
747
|
+
fetched on demand so a page nobody had open did not pay it every minute. Bitcoin Core publishes
|
|
748
|
+
everything the selection needs in the `getrawmempool(true)` reply this monitor **already reads
|
|
749
|
+
every 20 s** for the mempool view: `depends`, the ancestor sizes and fees, and
|
|
750
|
+
`fees.chunk`/`chunkweight` -- Core's own cluster-mempool linearization, which is the order its
|
|
751
|
+
miner sorts by. `server/collect/gbt.js` selects greedily over that, taking each transaction with
|
|
752
|
+
its unselected ancestors, and returns the result in the shape a `getblocktemplate` reply has, so
|
|
753
|
+
the summary, the histogram, the package analysis and the block economy read it unchanged.
|
|
754
|
+
|
|
755
|
+
Measured against the node's own template on a back-to-back pair at height 966821, so the two
|
|
756
|
+
describe the same pool: **6,546 transactions / 3,995,859 weight / 643,076 sat** against the
|
|
757
|
+
node's **6,535 / 3,991,951 / 642,860** -- 0.03% apart on fees, with the set difference confined
|
|
758
|
+
to the 0.30 sat/vB margin where ties are arbitrary. Assembly takes ~50-70 ms of this process's
|
|
759
|
+
CPU. It is a reconstruction of what a miner would choose, not the node's answer: sigop limits
|
|
760
|
+
and policy the mempool does not publish are not modelled, and the card says so.
|
|
761
|
+
|
|
762
|
+
The old measurement in `docs/MEASUREMENTS.md` -- that `getrawmempool` verbose carries no
|
|
763
|
+
`depends` -- was true of the experimental node it was taken on, and is kept there with the
|
|
764
|
+
correction appended rather than rewritten.
|
|
765
|
+
- **The default web port is 21000** (was 8088).
|
|
766
|
+
- **Display settings are stored on the server** in `config/blockyard.json`, so a phone and a
|
|
767
|
+
desktop pointed at the same monitor agree. The browser keeps a cache so boards still draw when
|
|
768
|
+
the server cannot be reached.
|
|
769
|
+
- **Block space ships with simple cubes and shadows off.** Shadows are the costliest single thing
|
|
770
|
+
the board draws, and the board is the first thing most people open; both remain one click away in
|
|
771
|
+
Display settings.
|
|
772
|
+
- The two games sit at the end of the nav under a **Diversions** pop-down, rather than among the
|
|
773
|
+
working tabs.
|
|
774
|
+
- The Markets energy pulse now runs along the neon price line itself, leaving an electric-blue
|
|
775
|
+
tail that fades back to yellow behind a bright head, with a nebula of blue smoke emitted along
|
|
776
|
+
the whole charged span and a shimmer over it. The lightning ball trails the same charge across
|
|
777
|
+
the block-space board; the light cycles do not.
|
|
778
|
+
- The pulse's nebula is emitted over the whole charged span rather than per segment — emitting per
|
|
779
|
+
segment gave neighbouring puffs the same age, so they shared a radius and lined up into the
|
|
780
|
+
concentric rings they were meant to replace. (Its motes and crackle branches were removed at the
|
|
781
|
+
same time and restored afterwards; they are present.)
|
|
782
|
+
- The Simple viewer packs the block exactly: the block's own area is solved so the tiles fill
|
|
783
|
+
the grid flush, and the remainder is tiled to the edge instead of leaving a partial top row.
|
|
784
|
+
- Pool attribution moved out of the block card's body into a readable pill beneath it.
|
|
785
|
+
|
|
786
|
+
### Since the 2026-09-11 milestone: fixed
|
|
787
|
+
|
|
788
|
+
- **Coinbase attribution stopped permanently after one failed block.** `pumpMining` cleared the
|
|
789
|
+
whole queue on a single failure and nothing ever re-queued it, so one slow moment discarded the
|
|
790
|
+
entire 36-block boot window and the Mining page sat empty. The failed height is put back, the
|
|
791
|
+
rest of the queue survives, and a backoff decides when to retry.
|
|
792
|
+
- **The block template no longer monopolises the RPC lane.** `getblocktemplate` went through as an
|
|
793
|
+
ordinary call with a 12-second freshness budget; on a node where it takes seconds, everything
|
|
794
|
+
queued behind it was stale-dropped and `/api/nextblock` took 75 s. It is now heavy, keyed and
|
|
795
|
+
given a realistic budget -- measured 52.8 s to 4.2 s on the same node.
|
|
796
|
+
- **The display-settings sliders no longer jitter while dragging** (operator: "the grid intensity
|
|
797
|
+
slider jitters when I move it"). Every `input` event ran a full synchronous re-render; a drag
|
|
798
|
+
across the grid intensity control queued forty of them, each repainting a board. The value and
|
|
799
|
+
the readout still update on every event — only the repaint is coalesced, to one per animation
|
|
800
|
+
frame. All twelve range controls were affected; the new one merely made it visible.
|
|
801
|
+
- **The Diversions menu renders correctly in Safari** (operator: "rendering on safari is still
|
|
802
|
+
broken. It's only showing half the drop-down contents"). The panel was inside `header.top`, which
|
|
803
|
+
is `overflow: hidden` and 46px tall, and WebKit clipped the fixed panel to it. It is a top-level
|
|
804
|
+
element now, like the settings dialog, which is the fixed overlay that always rendered correctly.
|
|
805
|
+
Its position is measured and set rather than pulled back by a transform.
|
|
806
|
+
- **Display-settings sliders jumped as their value changed**: the readout's width changed with its
|
|
807
|
+
digits and pushed the slider about. The value is printed to the step's decimals in a fixed-width
|
|
808
|
+
box.
|
|
809
|
+
- **The header's uptime blanked every second** and **the node you pick stays picked**.
|
|
810
|
+
- **The travelling cube's perspective froze in flight** (a regression of our own, recorded).
|
|
811
|
+
- **The pulse-gap simulation was flaky**: seeded now, its bound the real worst case.
|
|
812
|
+
- The block-being-built card named a call it does not make; lightning stopped whiskering; the
|
|
813
|
+
paint-order comments described a camera the viewer no longer has.
|
|
814
|
+
- **The star field never animated on a board that asked for no tile choreography.** `still` is
|
|
815
|
+
about the tiles; it was also returning before the animation loop started, so Tetrust's galaxy
|
|
816
|
+
repainted only when the page happened to redraw — measured at zero repaints in three seconds.
|
|
817
|
+
The loop now parks only when there is genuinely nothing moving. Measured after: 87 repaints in
|
|
818
|
+
three seconds, idle and in play.
|
|
819
|
+
- **Blocks swapped in front of each other during refreshes.** Where cubes overlap, the paint
|
|
820
|
+
order is solved as a graph; a cube flying past could pull a settled pair into a tangle and the
|
|
821
|
+
tangle was ordered by depth alone, discarding the pair's own decision. A tangle now keeps the
|
|
822
|
+
relative order it had in the previous frame. Replayed over a 634-frame transition: 19 flickers
|
|
823
|
+
to none.
|
|
824
|
+
- **Neon and the metallic sheen did nothing when switched on** — `render3d` never passed either
|
|
825
|
+
option through to the scene builder.
|
|
826
|
+
- The galaxy is much cheaper to draw: its gas is painted once into an offscreen bitmap and drawn
|
|
827
|
+
turned, and the stars are batched by colour and brightness instead of setting a fill style per
|
|
828
|
+
star.
|
|
829
|
+
- Blocks with no pool attribution showed no statistics at all.
|
|
830
|
+
- Taller cubes no longer clip the neighbour they lean over on a settled board.
|
|
831
|
+
- **The Explorer's Latest blocks cubes were drawn with faces that did not meet.** The top face and
|
|
832
|
+
the right face were each inset five pixels on two sides, so neither reached the top-right corner:
|
|
833
|
+
every block carried a dark triangular wedge there, a sliver of bare card at the top left and a
|
|
834
|
+
gap at the bottom right. The faces are now flush with the card and with each other.
|
|
835
|
+
- **The network hash rate read "0.0 EH/s".** Two bugs, one hiding the other. The estimator divided
|
|
836
|
+
difficulty by the average block gap and left out the 2^32 hashes a difficulty-1 target expects,
|
|
837
|
+
so it was out by a factor of 4.29 billion; and the formatter's unit prefixes were each one step
|
|
838
|
+
too low, so a four-digit EH/s figure would have printed as a single-digit one. Checked against
|
|
839
|
+
the node's own `getnetworkhashps`, which the monitor had never used: the corrected estimate is
|
|
840
|
+
1111.8 EH/s against the node's 1097.9, agreeing to 1.26%. The test covering it asserted that the
|
|
841
|
+
wrong magnitude was "of the right order", which is why it survived; it now checks the figure
|
|
842
|
+
could be true rather than restating the implementation.
|
|
843
|
+
|
|
844
|
+
## Milestone 2026-09-11 (labelled 0.9.0 internally; never tagged or released)
|
|
845
|
+
|
|
846
|
+
Licensed Apache-2.0 from here on. The block-space packer and feerate palette
|
|
847
|
+
are an original implementation (`public/js/blockpack.js`, `public/js/feepalette.js`).
|
|
848
|
+
|
|
849
|
+
### Monitor
|
|
850
|
+
|
|
851
|
+
- Live dashboard for one or more Bitcoin nodes over JSON-RPC, with an optional
|
|
852
|
+
log source: Overview, Chain & Sync, Mempool, Peers, Network, Mining, Events, Node & RPC and
|
|
853
|
+
Admin tabs, updated once a second over Server-Sent Events.
|
|
854
|
+
- Sync viewer whose bar is blocks held over announced headers, with the node's own progress
|
|
855
|
+
shown separately and an ETA computed only from a measured rate window.
|
|
856
|
+
- Block flow: projected blocks beyond the one being assembled (fee range, median, fees,
|
|
857
|
+
transaction count, time estimate), the block being built with its age ring, and recent
|
|
858
|
+
blocks linked as a chain.
|
|
859
|
+
- Peer table from `getpeerinfo`: direction, transport, services, height at connect, bytes and
|
|
860
|
+
rates per connection.
|
|
861
|
+
- A provenance table naming the source of every figure, and explicit "not reported" markers
|
|
862
|
+
instead of zeros.
|
|
863
|
+
|
|
864
|
+
### Block space viewer
|
|
865
|
+
|
|
866
|
+
- A 3D board of the next block's worth of the mempool: square tiles sized by vbytes and
|
|
867
|
+
coloured by feerate, on a curved, neon-gridded board.
|
|
868
|
+
- 128 feerate colours from under 0.1 to 2,000 sat/vB, sky blue through green, yellow, orange
|
|
869
|
+
and red to purple, with neighbouring bands stepped in tone so they read apart.
|
|
870
|
+
- Choreographed refreshes: blocks lift, travel in collision-free lanes and land under gravity
|
|
871
|
+
with bounces; cube-on-cube shadows; a refresh countdown and a "refresh now" button.
|
|
872
|
+
- Idle effects at rest: ripples, scans, tides, cascades, twinkles, TRON light cycles and a
|
|
873
|
+
lightning ball that runs along the grid lighting the cubes it passes.
|
|
874
|
+
- Viewer modes: **Simple** (the richest few hundred transactions as cubes, the rest as
|
|
875
|
+
equal pieces coloured by their feerate) and
|
|
876
|
+
**Detailed** (every transaction in the next block, one square each on a 96-unit
|
|
877
|
+
grid, drawn as low slabs, each square area-true so a full block fills the board),
|
|
878
|
+
remembered per browser.
|
|
879
|
+
|
|
880
|
+
### Explorer
|
|
881
|
+
|
|
882
|
+
- Search by block height, block hash, transaction id or address; every page is a shareable
|
|
883
|
+
link (`#explorer/…`).
|
|
884
|
+
- Transaction pages: status, fee and fee rate with dollar values, feature badges (SegWit,
|
|
885
|
+
Taproot, RBF, consolidation, OP_RETURN, coinbase), a flow diagram from inputs to outputs,
|
|
886
|
+
inputs and outputs with links to the spent and spending transactions, copy buttons.
|
|
887
|
+
- Block pages with statistics and paged transactions; address pages with balance, totals and
|
|
888
|
+
history (with the node's address index); the latest blocks as fee-coloured cubes.
|
|
889
|
+
- Links into the explorer from the rest of the app.
|
|
890
|
+
|
|
891
|
+
### Markets
|
|
892
|
+
|
|
893
|
+
- Prices from Coinbase, Kraken, Bitstamp, Bitfinex and OKX, fetched by the server only while
|
|
894
|
+
someone has the Markets or Kiosk tab open.
|
|
895
|
+
- A 3D candle chart on a low side-on camera with a neon close line, volume band, labelled
|
|
896
|
+
price levels, a star field and light from the front right.
|
|
897
|
+
- A flat candlestick chart with a crosshair readout and other exchanges overlaid; an exchange
|
|
898
|
+
table; 24 h / 48 h / 7 d ranges.
|
|
899
|
+
- An order-book depth chart: cumulative bids and asks per exchange and in total, the total as
|
|
900
|
+
it stood 1–60 minutes ago, and change bars on a symmetric-log axis.
|
|
901
|
+
|
|
902
|
+
### Kiosk
|
|
903
|
+
|
|
904
|
+
- The 3D markets board, a price panel and the block-space board side by side, with a
|
|
905
|
+
full-screen button.
|
|
906
|
+
|
|
907
|
+
### Display settings
|
|
908
|
+
|
|
909
|
+
- A gear in the header opens a settings panel: shadows, idle effects, stone edges, the neon grid,
|
|
910
|
+
a star field, level of detail (full / simple cubes / flat tiles), refresh animation
|
|
911
|
+
(full / quick / none) and board curve for the block-space board; star field, density, brightness
|
|
912
|
+
and grid glow for the markets board. Kept in the browser, applied without a reload, and each one
|
|
913
|
+
changes what is drawn rather than only what is stored.
|
|
914
|
+
|
|
915
|
+
### Security
|
|
916
|
+
|
|
917
|
+
- Open, read-only access by default; optional accounts with scrypt hashing, hashed sessions,
|
|
918
|
+
CSRF protection, lockouts and a rotated audit trail.
|
|
919
|
+
- Default-deny RPC allowlist; node writes off unless explicitly enabled per action.
|
|
920
|
+
- Built-in HTTPS, multi-address binding, a CIDR gate, and a strict Content Security Policy
|
|
921
|
+
with no inline styles and no third-party assets.
|
|
922
|
+
|
|
923
|
+
## Milestone 2026-09-08 (labelled 0.1.0 internally; never released)
|
|
924
|
+
|
|
925
|
+
Internal first version: multi-user monitor with charts, sync viewer, mempool view, peers and
|
|
926
|
+
event feed.
|
|
927
|
+
|
|
928
|
+
[0.0.10]: https://github.com/BobClawblaw/blockyard/releases/tag/v0.0.10
|
|
929
|
+
[0.0.9]: https://github.com/BobClawblaw/blockyard/releases/tag/v0.0.9
|