Repository navigation
Releases: BobClawblaw/blockyard
Release list
BlockYard 0.1.5
BlockYard now ships on Simple mode. A fresh install draws its 3D boards with the new Simple
renderer: the same cubes under the same camera, flat and still, painting a frame only when the data
or the pointer changes, so a monitor left open on a laptop or a wall costs the graphics card nothing.
There is a lot of visual fun to discover if you want to explore: Settings (the gear) ->
Appearance -> 3D renderer -> Software or WebGL brings back everything the 3D engine does --
the Galaxy, Formation, Sun and Earth skies, thirty-odd idle effects, blocks that fly and bounce into
place, neon, chrome and satin finishes, and the price board's black hole, supernova and ball
lightning. WebGL is the one to try on a good graphics card.
Changed
- npm is retired; GitHub is the only release channel. The
blockyardpackage on npm stops at
0.1.3 and is deprecated there.package.jsonis nowprivate, sonpm publishrefuses it. An npm
install moves to a checkout without copying anything:node bin/blockyard.js startfrom the
checkout reads the same~/.blockyard(docs/INSTALL.md §2). - The Simple renderer ships as the default (Settings -> Appearance -> 3D renderer), with its slide between
layouts off: a resting board paints no frames at all, on any graphics card. Software and WebGL are one click away.
A saved settings file keeps whatever it chose, so an upgrade changes nothing for anyone who has saved settings.
Fixed
- Quality flags no longer outlive their cause. Seen 2026-09-24 on a healthy mainnet bmc at
93% of its sync, whose node page showed five warnings, none of them current:rpc-timeoutscounts failures in the last 10 minutes, not since the monitor started. 48
refused connections from one three-minute node restart read as "the node is under load"
three hours later.tip-staleclears when the chain advances. bmc need not ever log "tip fresh again".- A block archive laid out out of height order (how a parallel download writes it) is an
infofact, not a warning, and is no longer counted a second time as acheck-problems
problem. Real damage (a frame that will not read, a body that does not hash) still warns. - The four relay line shapes (relay legs, accepts and rejects, orphans, address gossip) are
watched for silence only outside IBD, as the two download shapes are watched only inside it. nettotals-zerowaits until the node has been up two minutes with peers connected. A node
that has just restarted has moved no bytes yet, and the flag blamed the build for it.inbound-handshake-failingwarns at 10 failures in 10 minutes and clears when the rate
drops. It used to go up on the first failure and stay: 3 dropped handshakes in a node's
first minutes of listening are what any listening node draws.
Added
- A third renderer, Simple (Settings, Appearance, "3D renderer";
2.5din a saved settings file), for a machine whose graphics
are not to be worked at all. The same cubes under the same oblique camera, flat: no sky, no idle
effects, no flight between layouts, no shadows or finishes, the price line as one stroke. It
paints one frame when the data or the pointer changes and nothing in between, so a resting board
costs no frames a second on either the processor or the graphics card. Hover, click-through and
a game's own overlay work as before. The page's own pulses (the live dot, the block rail and
meter, the overdue warning) hold still with it: with every board parked, those alone kept the
page compositing 60 frames a second on the graphics card. "Simple sky": Still (the default) is
a star field drawn once and left alone, no galaxy, nothing turning; Slow keeps the board's sky and
repaints it twice a second from a timer, about a fiftieth of the shipped sky's frames, so the
stars drift and the Earth sky's day passes; Off is a plain dark panel. The Formation and the Sun
stand down to the star field there. Measured on the live monitor, composited frames in ten
seconds: Block space 600 with the shipped sky, 14 with the Simple sky off, 34 with it slow.
Measured in headless Chromium at 2560x1300 under the shipped sky and effects, frames painted in
thirty seconds at rest: 0, against 1,875 on Software (scripts/renderer-25d-check.mjs); whole
pages of the live monitor, composited frames in ten seconds: Overview 600 -> 6. - Software and WebGL are much lighter on the graphics card. Three changes, each measured in
Chromium on Block space at 1600x1000 CSS pixels and device scale 2, over 10 seconds:- The page's pulses tick instead of animating (Settings, Appearance, "Page pulses": Tick is
the default, Smooth is the old continuous fade, Still holds them). The live dot, the block
meter's edge and the overdue warning were endless CSS animations. Any endless animation makes
the browser redraw the whole page at every refresh of the screen, 120 times a second on a
ProMotion display. Now one attribute flips every 1.3 seconds. Software went from 1,206
composited frames in 10 seconds to 426. Stepped CSS timing was tried first and did not help
(1,202). The Simple renderer keeps its pulses still, as before. - WebGL draws at one pixel per CSS pixel (Settings, Appearance, "WebGL resolution": 1x is
the default, with Full and ¾x). On a Retina screen that is a quarter of the pixels. The GPU
process went from 33% busy to 21.5%, and its compositor from 25-31% to 17%. The glow setting
made no measurable difference. - A frame-rate cap (Settings, Appearance, "Frame rate cap": 60 is the default, with 30 and
Full). A moving board paints at most 60 frames a second instead of at every refresh. A resting
sky still repaints at most 30 a second. Between paints the board now waits on a timer rather
than asking for animation frames it will not paint, because every frame asked for is a frame
composited. On a simulated 120 Hz display, a resting sky asked for 60 frames a second to paint
30; now it asks for 30.
- The page's pulses tick instead of animating (Settings, Appearance, "Page pulses": Tick is
- The Detailed board is Core's block, strictly ordered. It used to be the mempool sorted by each
transaction's own fee rate and cut at 1,000,000 vB. That put a child paying for its parent (CPFP)
where its own rate said, and left the parent out. On Core's pool on 2026-10-01, 316 of about 3,600
transactions on the board would not be in Core's block, and 317 of Core's were missing. The board
is now the block the "being built" card shows: Core's chunk fee rate, each package whole, and
Core's own fill at the end of the block. On the same pool it was within 5 transactions and 0.06% in
fees of the node's owngetblocktemplate. Each square is ordered and coloured by the rate its
package was taken at, and no square sits below one that pays more. - The block is assembled once per pool update, in about 60 ms instead of 260–400 ms. It stops as
Core's block assembler does (after 1,000 misses in a row once the block is within 4,000 weight
units of full), and its sort no longer useslocaleCompare. The board and the "being built" card
share the result. - The Detailed board's scale is fitted to within 0.2%. In strict order the only empty space is
along the block's top edge. The 5% scale steps could give away up to 5% of the board there; a
finer search now closes in on the smallest scale that fits. scripts/manage-users.jsreads a piped password. It treated every chunk on stdin as one
keystroke, so a password piped in (echo "$PW" | node scripts/manage-users.js create alice)
arrived as one chunk. The newline went into the password, the prompt never finished, and the
script exited having done nothing. It now reads character by character, and the end of the input
also ends the password. Found as a test that failed only under load.- Three tests no longer fail under load. Each was reproduced with every core busy and then
fixed at the cause:- the user-management test was the piped-password bug above (6 of 12 failed, then 0 of 12);
- the RPC lane test now asserts the rate ceiling against submission time, not the gap between
two actual starts, which one late timer can shrink (3 of 16 failed, then 0 of 16); - the index-resume test's crash snapshot now retries when a file vanishes under the copy while
the build's other worker renames it (1 of 16 failed, then 0 of 32).
- Steady (5 s flight), a Refresh animation between Rapid (3 s) and Quick (10 s). On a found block
over the live pool, with 2,243 blocks moving, it settled in 4.9 s. - Board order (Settings, Block space): Exact, the default, lays the board out afresh on
every update, richest at the bottom, so the order is always the fee order and most blocks move
each time. Stable keeps every block still waiting in its place and puts new ones in the free
space: far fewer moves, but a rich newcomer can sit above cheaper blocks until the board is
re-sorted. That was the behaviour earlier the same day; the operator saw the board out of order
("not accurately redraw in order of most expensive to least expensive"). - Effects on the Detailed board cost 20-36% less. Three changes, each checked pixel for pixel
against the old renderer:- The WebGL colour cache no longer thrashes. It held 8,192 colours and was cleared when full;
a Detailed board has more than 11,000 distinct shades, so every frame parsed every colour again.
It now keeps two generations instead. - A resting cube's faces are reused from frame to frame while an effect is not touching it.
- A retained layer: while an effect only tints or lights some cubes, the resting board is kept
(on the graphics card, or in Software's layer), and only the touched cubes are drawn over it.
Effects that lift, hide, shrink or pull cubes, draw a ring into the floor, or make cubes
see-through are still drawn whole, because a cop...
- The WebGL colour cache no longer thrashes. It held 8,192 colours and was cleared when full;
BlockYard 0.1.4
A fifth AI security audit, every finding fixed the same day — brutally honest and extremely
technical by request, with the DOS Diversions (the x86 interpreter and everything around it) read
hardest of all: it came back the best-defended part of the codebase (a provably sound memory
model, no eval/Function/WebAssembly anywhere in the client, a corrupted game binary contained
to its own worker), with one real gap, now closed. 1 HIGH, 4 MEDIUM, 10 LOW/informational, 15 in
total, all fixed against a regression test that failed first
(SECURITY-AUDIT-2026-09-22.md,
REMEDIATION-2026-09-22.md). The headline fix reaches past the
finding that prompted it: the RPC allowlist that keeps every wallet method off this monitor is now
enforced by the RPC transport itself, not only by the route that happens to check it first, so no
future code path can reach a wallet method by skipping a check. Two effect-timing bugs from 0.1.3's
WebGL work are fixed alongside it.
Security
- The RPC transport itself refuses a wallet method with no explicit authorization, not only
the console route in front of it. Verified against a real regtest node's wallet flow, not a mock. - The audit trail is hash-chained. Each entry's hash covers the one before it, so an entry
edited or removed in place is detectable —npm run verify-auditchecks it. Tamper-evident, not
tamper-proof: there is no secret key, and the report says exactly what that does and does not
cover. - Login gets the same cross-site protection every other state-changing route has. A cross-site
form could previously log a visitor's browser into an attacker-chosen account. - A reverse-proxy documentation bug that would have let a client spoof the address the rate limits
and the CIDR gate trust, oncetrustProxywas turned on following the (now-fixed) example. manage-users.js passwd/disablerevoke a user's existing sessions, matching the web routes.- SSE responses carry the same CSP and security headers every other response does.
- The DOS Diversions' executable loaders validate header fields against the real file length
instead of trusting them. /games/*is rate-limited;Cross-Origin-Opener-PolicyandCross-Origin-Resource-Policyare
sent; an IPv6 /64 can no longer rotate past the login throttle; a decoded backslash is refused in
static file serving on every platform, not only where it would matter; a file descriptor leak and
a missing bounds check in the address-index build; a dead exception clause in the RPC allowlist;
a response-size cap on the pool-map refresh script.
Fixed
- The pulsar wind effect's duration now follows its (much wider) span. 0.1.3's fix for effects
blinking in and out on the Markets board widened the pulsar's crossing to fly from beyond one
edge of the panel to beyond the other, without lengthening its time to match — so the same
duration now covered several times the distance, and the passage rushed by. Its duration is
derived from the actual span it has to cross, so a wide panel gets a proportionally longer
passage instead of a faster one. - The black hole's orbit runs on the effect's own clock, not the page's. The candles/cubes it
pulls in span were timed off the page's own uptime (performance.now()since load, unbounded,
never reset) rather than the effect's elapsed time — invisible on a freshly loaded page, and
increasingly wrong on a Kiosk display or any tab left open for hours, where the same brief
capture ramp could whip everything round many extra turns before settling. Fixed to read elapsed
time off the effect's own bounded progress.
1,468 tests, from 1,445.
BlockYard 0.1.3
BlockYard 0.1.3 — 2026-09-22
A release in two halves. The first is the skies and the renderer: every 3D board, sky and
effect now draws on either of two renderers -- Software, as before, or WebGL, the same
picture on the graphics card, many times faster on a big display, with a glow of its own -- and two
new skies stand behind the boards: the Formation, a galaxy assembling itself after the TNG50
film, and the Sun, our star as the Solar Dynamics Observatory sees it at 171 Å, built as a
model rather than a picture -- differential rotation, sunspots, three-dimensional coronal loops,
flares, filament eruptions and mass ejections after the observatory's own films, prominences,
coronal rain, an activity cycle. The block finishes were rebuilt (chrome is polished steel now,
satin brushed metal, neon real tubes), ball lightning throws real lightning, the supernova is NASA's
frame by frame, and the Markets page gained 1 h, 3 h and 12 h charts and a sixth exchange,
Gemini. The second half, prepared on 2026-09-19, is about the node BlockYard was built beside:
Bitcoin Machine Code -- an experimental, Core-compatible node in x86-64 assembly -- is named as
BlockYard's first-class companion; the monitor reads every line of its log (100% of every current
log, where it read 53% a release ago) and its RPC is measured against Core's. The RPC lane
keeps up to four calls in flight per node, a run of chart fixes came from watching a syncing
node beside a synced one, Wolfenstein 3D's menu takes keys again, and every shareware game
ships whole with its own terms. The administrative suite grew to M7 on the way and is excluded
from this release, as it is from every release. 1,445 tests, from 1,070.
Upgrading from 0.1.2
- The RPC lane now keeps up to four calls in flight per node (
rpc.maxInFlight, default 4,
matching Bitcoin Core's four RPC threads). It does not ask any node for more calls a second.
A node that serves one connection at a time should get"rpc": { "maxInFlight": 1 }on its own
entry. - One more outbound host, only with market polling on:
api.gemini.com, for the sixth exchange. The
security notes list every host contacted. - Nothing to migrate. No setting was renamed, no file moved. The renderer ships on Software, as before;
WebGL is a choice in Display settings, Appearance. - A fresh server starts at the shipped defaults. Since 0.1.0 a server with no settings file took whatever the
first browser to reach it remembered from an earlier install -- which on a laptop reused for a fresh clone meant
shadows, the metallic sheen and market polling all on. It starts at the defaults now (shadows off, simple cubes,
sheen off, polling off), and nothing is uploaded until someone changes a setting. An install that already has
config/blockyard.jsonis untouched; reset in Display settings puts it back to the defaults. - The Markets board ships under the Formation, in the bottom-left corner.
Added
- A second renderer: WebGL. Settings, Appearance, 3D renderer: Software (the 2D canvas,
as before, and still the default) or WebGL. Every 3D board, sky and effect draws on either:
the viewer's drawing calls are implemented a second time on WebGL2 (public/js/gl2d.js), so an
effect is written once and runs on both. The switch is live, and a browser without WebGL2, a
shader that will not compile or a lost graphics context goes back to Software by itself, so a
board is never blank. Checked in a real browser, the same frame on both renderers, for every
sky and every effect on both boards (node scripts/gl-compare.mjs): mean difference under 2 in
255 per channel. Scorched Yard's land and sky stay on Software: the game reads their pixels.
WebGL also has a finish of its own, WebGL glow: neon lines, stars, sparks and white-hot cores
throw real light (bloom), and wide faint glows lose their bands. The blocks never glow: their
colour is the feerate. - The Formation, a fifth sky: a galaxy assembling itself on a loop, after the TNG50 film. On a
graphics card it is the film's own main view -- the gas as a glowing density field, black to
violet to magenta to orange, a pale spiral galaxy at the middle, satellites falling in -- and
where there is no graphics card it is drawn as specks on the 2D canvas, in the same colours, in the
same place, and as perpetual. Its galaxy can sit
behind the board or in any corner (Formation centre), it has its own brightness and flow
sliders beside Formation speed, and thirteen palettes (Formation colours): the film's own, and twelve
measured to stay clear of the chart's green, red and yellow. It never loops and
never fades: satellites keep falling in on new orbits, shed wakes and stir the gas they cross. - The Galaxy sky on WebGL also has light between its stars (a haze along the arms, a warm bulge), stars
that are soft points with halos and diffraction spikes on the brightest, and distant galaxies that are
smudges of light rather than stacked ellipses. - The Galaxy sky's nebulae are gas on WebGL: billowing clouds with filaments, hollows and a second hue,
and ragged dust lanes that darken what is under them, where Software stacks translucent ellipses. And a
Rotation speed slider for the spiral, from still to a turn in under a minute. - The lightning ball's lightning is lightning: channels that hold their shape while they slam on, re-strike
and die, tortuous at every scale, forking into hair-thin branches, several alive at once, with a flare where
each lands. On WebGL they glow. (It was a handful of zig-zags re-randomised every frame.) - The supernova on WebGL is after NASA's animation frame by frame: a smooth white-hot breakout with a cyan
rim, opening into lavender and violet billows round a magenta heart, soft blue gas ahead of it: a continuous
volume of gas where there were soft blobs. Ball
lightning throws real bolts on both boards -- channels that hold their shape, fork and re-strike -- crackles with
held tendrils, carries sparks in orbit, and on Markets flies in from beyond the screen's edge and out past the other.
The grid-tracing ball is the Plasma ball now and throws none. - The Sun, a new sky (Settings, Sky): our sun as the Solar Dynamics Observatory sees it at 171 ångström, in
gold -- the bright lace of the magnetic network over boiling plasma, turning faster at its equator than at its poles,
darkened toward its limb, with streamers off it -- and active regions: white-hot footpoints with fans of coronal
loops, carried round at their own latitude's rate, flaring, their loops standing off the limb; and filament
eruptions, after the observatory's own films: a dark filament lights, lifts and is flung off as a red arch of
plasma behind a bright front, ribbons and an arcade of loops light where it was, and the corona dims -- about half
fail and fall back. Its loops are true three-dimensional arches (they foreshorten, cross, and stand tall on the limb);
there are sunspots, red prominences that show as dark filaments on the disk, coronal rain, flares with their flash,
diffraction cross and wave, helmet streamers and polar plumes, an activity-cycle slider from solar minimum to maximum,
five other SDO channels as colour palettes, and a detail setting for slower graphics. Sliders for its size (from a small disk to its surface
filling the whole panel as the background), brightness, rotation and position. The research and the plan aredocs/PLAN-SUN-SKY.md. - Markets: 1 h, 3 h and 12 h charts, made of 1-, 5- and 15-minute candles (about sixty bars each) from the same
five exchanges, on both the flat chart and the 3D board, with minute labels on the time axis. The server fetches a
finer grain only while a chart that short is open (/api/markets?tf=), and stops ten minutes after the last ask. - The Kiosk follows the Markets page: the exchange and range chosen on Markets -- on this browser or any other --
reach an open Kiosk within seconds, without a reload; its caption gives the chart's real span and bar size. - Gemini is the sixth exchange on Markets: its price, candles at every grain and its whole order book. With market
polling on, the server now also contactsapi.gemini.com(the security notes and the Settings text list every host). - The block finishes on WebGL: Chrome is new -- a room's strip lights in polished steel, soft bands running
across the whole board, on both renderers -- and costs what a plain board does (it was twice that); Satin is
rebuilt as brushed metal (a sheen across every face, machined bevels, a brushed grain on WebGL); Neon is one
soft stroke an outline with a white core, a coloured tube and a halo (a live neon board 38.7 -> 9.2 ms a frame on an RTX 5090). - Show frame rate (Settings, Appearance): frames painted in the last second, the processor's
milliseconds a frame and the renderer, top right of every 3D board.
Fixed (2026-09-22)
- The Kiosk follows the Markets page. The exchange and range chosen on Markets -- on this browser or any
other -- reach an open Kiosk within seconds, without a reload. Display settings were fetched once, at page load,
so a Kiosk on a wall kept the old choice for days; every open page now re-reads them every fifteen seconds and
when its tab comes back into view (never over a change it is still saving). The Kiosk's caption gives the chart's
real span and bar size -- it counted candles as hours, so a 3 h chart of five-minute bars read "last 36 h". - The price was cut off on a short chart. The room kept beside the 3D candle board for the price tags was nine
grid units; with sixty one-minute bars a unit is six pixels on a tablet and the current price ran off the panel.
It is a share of the panel now, with the old nine units as the floor. - **An old...
BlockYard 0.1.2
A day of hardening, and a new game. The headline is the third AI security audit, run on
2026-09-16 over the whole codebase by four auditors in parallel, and every one of its findings
fixed the same day with a test that holds it: one high (a client that opened the live stream and
stopped reading was never dropped, so memory grew without bound), eight medium, seventeen low and
six informational. The address index learned to resume an interrupted build instead of starting
its hours-long read again, and is now checked in the suite against a second, deliberately naive
implementation; property tests over Bitcoin Core's file formats found and fixed three flaws in
reading undo data. The skies were made clear: every board picks the Galaxy or the Earth sky in
one place, and the Earth sky gained a warm sun with a halo, a moon that glows through its real
phases, and softer clouds. And under Diversions, Scorched Yard: Scorched Earth on the block
engine, with the manual's weapons and computer players who each wear their own colour and aim like
people, up to eight tanks, a cheat mode that draws
the firing solution, and wind that is a real fluid simulation flowing over the hills. The games now
load only when their page is first opened. 1,070 tests, from 945.
Upgrading from 0.1.0
- Node.js 22.2 or newer is required (was 22.0): the resumable index build uses Node's built-in
CRC-32. - With accounts off, the node connection form works only from the monitor's own machine
(127.0.0.1).auth.openNodeConfigFromNetwork: truerestores the old reach; with accounts on,
an admin can use it from anywhere, as before. - Wallet RPCs are refused in the RPC console, reads included (
getbalance,listunspent, …). - The shipped systemd unit is sandboxed. If you install it again, edit its
ReadWritePaths=
lines to your install'sdataandconfigdirectories, and add your address index directory. - The address index build refuses a directory that holds anything an index does not write, a
symlink, or a home, working or blocks directory. PointaddressIndexat a new or empty
directory, or at an existing index. - Sky settings are migrated from the old global sky and per-board star switches to one Sky
choice per board; each board keeps what it drew. scripts/pool-map.jsasks before replacing the pool map (--yesto skip), and
scripts/setup.jstakes the RPC password from--rpc-password-fileor stdin; the plain
--rpc-passwordstill works, with a warning.
Security
- The third audit's low and informational findings, fixed the same day (
docs/SECURITY-AUDIT-2026-09-16.md, tests intest/audit-2026-09-16-low-*.test.js), so every finding of that audit is closed. Browser: the post-sign-in redirect stays on this site; the page's key-value lists escape every value unless it is markup the page built, and the server passes the node's chain name, pruned and IBD flags and unbroadcast count on only in their own types; RPC URLs shown to viewers lose any username and password; viewers see the last two parts of cookie, log and settings paths; a malformed explorer link opens the explorer's home. HTTP: a malformed escape in a route is a 400; the sign-in lockout is per username and address, so one address can no longer lock an account for everyone (an address trying many accounts, or an account tried from many addresses, still locks at a higher count). Collectors: a coinbase with a truncated push no longer throws or stalls pool attribution; an order book far from the others is not drawn, the depth grid is capped and market responses are size-capped with redirects refused; long log lines can no longer take seconds to parse. Index: the height table is sized from the tip and cannot loop; a transaction whose input count disagrees with its undo record fails the build; a lock file stops two builds sharing a directory; files are created owner-only and temporary files are never written through a symlink;--workersis validated; malformed self-written files are cut or skipped rather than crashing. Scripts and supply chain:setup.jsno longer echoes the RPC password and takes it from a file or stdin;pool-map.jsrefuses non-HTTPS redirects and shows a diff before replacing the map; CI actions are pinned to commit hashes;config/*.bak-*is ignored; the game files have a hash manifest.SECURITY.mdnames 0.1.x as supported, and the smoke script's open-access instance now really opens access - The third audit's high and medium findings, fixed the same day (
docs/SECURITY-AUDIT-2026-09-16.md, tests intest/audit-2026-09-16.test.js). Stalled stream readers: a client whose socket is full is sent nothing more until it drains (the newest snapshot waits, as the stream's comments always claimed), and is dropped at 4 MB buffered or after a minute blocked; one address or account holds at most 16 streams. Open mode and scripts: with accounts off, the node connection form (save and test) answers only a caller on this machine's loopback address, since a browser check cannot stop a script;auth.openNodeConfigFromNetworkrestores the old reach. A save that moves the node to another host drops the old endpoint'srpcUser,rpcPasswordandcookieFile, and testing a foreign endpoint reports the kind of failure, never what it answered. The index build refuses a symlink, the filesystem root, a home or working directory, the node's blocks directory, or a directory holding anything an index does not write, and only ever removes its own files. Wallet RPCs are all refused by name, reads included. Request bodies must arrive within 30 seconds; the event stream is unaffected. Audit rows clamp every string to 1,024 characters. The npm package no longer includes the gitignored private notes, and a test holds it to tracked files. The shipped systemd unit is sandboxed (ProtectSystem=strictwithReadWritePathsfordata/andconfig/,ProtectHome=read-only, no capabilities, a system-call filter,UMask=0077), and the app was started and exercised under exactly those settings - Third AI security audit (
docs/SECURITY-AUDIT-2026-09-16.md), every finding fixed the same day (above). One high: a client that opens the live event stream and stops reading is never dropped, and 400 such connections took a test instance from 73 MB to 1.5 GB in three minutes. Eight medium: in open mode a script (not a browser) can rewrite the node connection and receive the node cookie after a restart, and can use the connection probe to read internal URLs; the address index build deletes its output directory recursively without checking it; the RPC console admitslistdescriptorsandgethdkeys, which return private keys from an unlocked wallet; request bodies have no read deadline; oversized RPC method names flush the audit trail; the npm package includes the gitignored private notes; the shipped systemd unit is barely sandboxed. Seventeen low and six informational. Both earlier audits' fixes re-verified
Address index
- An interrupted address index build resumes. Stopping BlockYard, Ctrl-C in the installer, a crash or a reboot used to throw away the whole build, which reads about 880 GB and runs for hours on a shared disk. The build now keeps a small journal beside its output, written atomically with a checksum of its own contents, and the next start carries on from it: block files already scanned are not read again, and buckets already sorted are not sorted again. Nothing it cannot prove finished is used: the bucket files are cut back to the length the journal recorded, so rows from a file that was half appended are gone, and a bucket about to be sorted is checked against the length and CRC-32 the scan recorded. The resumed index is byte for byte the one an uninterrupted build writes, which the tests check after stops at seven points, from the directory the stopped build left and from a copy taken at the instant of the stop. A journal for another index format or file selection, for a tip the node's chain no longer has, or that is torn or damaged is thrown away, the log says why, and the build starts over. The manifest is still written last. The progress and its ETA pick up from the resumed position. Also fixed on the way: a worker that died between two jobs left the pool waiting for it forever, and when two things went wrong at once the build reported the second rather than the cause
- Fixed: three flaws in reading Bitcoin Core's undo files, found by new property tests. The suite now generates thousands of seeded encodings (CompactSizes, Core VARINTs, compressed amounts, undo records, legacy and segwit transactions, blocks, block and undo files), checks each decoder against an oracle written from Core's format, and truncates and flips bytes to hold every decoder to throwing or decoding exactly. An output amount above about 10 million BTC was rounded when read, and is now exact. An undo record cut inside its last script was accepted as whole, and is now refused. A corrupt VARINT could decode as an enormous height, and is now refused past Core's own 32-bit limit. No real chain data reaches the first flaw
- The address index is checked automatically against a second implementation. Until now its correctness was checked by hand, with scripts against a live node, and history had no independent check at all. The suite now builds a seeded synthetic chain (301 blocks, 6,346 transactions, 384 addresses across eight script types, XOR-obfuscated block and undo files with a stale block and reordered, duplicated records), indexes it with the real build, and compares every address's balance and whole history, walked through the store's paged queries past the counting limit, with a deliberately naive replay of the UTXO set that shares no indexing code. It compares again after the live follower folds and merges sixty more blocks, after a reorganisation inside the tail ...
BlockYard 0.1.0
The first minor release, two days after the initial 0.0.9, and everything since it in one place:
the 0.0.10 that was prepared on 2026-09-15 was never published, and its changes ship here. Four
things shaped it. The first outside review of 0.0.9 on bitcointalk, which called out a monitor
that bound every interface with no sign-in: 0.1.0 ships hardened — this machine only, sign-in on,
HTTPS with a certificate it makes itself, and no outbound connection at all until the exchange
feed is switched on. A day of use on a laptop screen, which found every tab a scroll: every
page now packs to one screen at 1093×945, and the Mining tab follows mempool.space's dashboard
with its View more panels. Appearance: the layout's colours are a setting — light or
dark, five themes, or nine colours of your own. And the DOS Diversions: Wolfenstein 3D, DOOM
and Quake, unmodified shareware on a PC emulated in the browser, written here with no
dependencies, because "can it run DOOM?" deserved a straight answer. 944 tests, from 862.
Upgrading from 0.0.9: docs/INSTALL.md §11 — the first start after
the update serves HTTPS, creates the admin account and prints its password once.
Security
- Hardened defaults (after the first outside review of 0.0.9): the monitor binds
127.0.0.1
and requires sign-in out of the box — the first start creates theadminaccount and prints its
password once (or takesBLOCKYARD_ADMIN_PASSWORD). Reach it from elsewhere over an SSH tunnel,
or bind a LAN address withBLOCKYARD_BIND/server.hosts; open read-only access is a choice
(BLOCKYARD_AUTH=0), announced at boot. The boot banner says how to reach a loopback-only
monitor. Existing installs keep whateverconfig/local.jsonsays. - HTTPS by default. Every listener serves HTTPS; with no certificate named, the first start
makes the monitor its own self-signed one under<data>/tls/— written without dependencies
(server/tls/selfsigned.js: a v3 X.509 certificate assembled in DER and signed with ECDSA P-256
over SHA-256), naming the bound hosts, this machine's addresses and hostname — and remakes it
near expiry or when a bound address is missing from it.blockyard tlsremakes it by hand
(--santo add names,--printto export it). Your own certificate still replaces it;
BLOCKYARD_TLS=0serves plain HTTP behind a proxy. The installer's summary and port probe know
both schemes. - The installer asks twice before a non-loopback bind. A LAN or wildcard address is written
only when the operator types it and confirms it; the default it offers is127.0.0.1. - Market polling is off by default — a new checkbox, Display settings → Markets & Price →
Enable market polling, shared by every screen. A fresh install makes no outbound connection
but to the node: the Markets and Kiosk tabs say that polling is off and where the switch is,
Overview's price line stays hidden and the explorer shows no dollar figures. Ticking it takes
effect at once; unticking parks the feed at once.BLOCKYARD_MARKETS=0still removes the feed
so that the checkbox cannot turn it on. Existing installs tick the box once.
Appearance
- A first tab in Display settings for the colours of the layout. A theme mode — Light, Dark,
or System, which follows the operating system and changes with it — and theme cards with swatch
strips of the face the mode picks: BlockYard (the shipped look, the default; its dark face
is value for value what the monitor drew before), Mono (clean grayscale), Nous (GitHub's
chrome with a Nous blue accent), GitHub (Light Default and Dark Default) and Catppuccin
(Latte and Mocha). Custom is nine colour pickers — page, panels, text, muted text, accent,
lines, good, warning, bad — with the rest of a face derived from them (a raised panel, the
softer rules, the fainter text, the darker accent of a pressed button), and whether Custom is
light or dark read off its page colour; customise copies the theme on screen into
the pickers so a custom scheme starts from a look. Saved on the server with the other settings,
so every screen of the monitor draws the same; the sign-in page draws in the theme the browser
last saw. - The theme is applied through the CSSOM (the page's custom properties rewritten on
<html>),
which the content-security policy permits where a style attribute is refused. The charts' axis
text, grid lines and tooltips, the order-book depth chart's two sides and the market charts'
grounds follow the theme — they were literals of the dark look, and a light theme would have
drawn grey-on-grey. The 3D boards stay space, and the Explorer's block and transaction pages
keep their own dark cards.
Mining
- The Mining tab has the network row: reward stats over the last 144 blocks, the difficulty
period (blocks remaining, the estimated and the previous change, the next halving), a week of
pools with luck and counts, a year of hashrate with the difficulty's steps, and the last twelve
adjustments as a table — every figure from this node alone (server/collect/network.js:
getblockstatsfor the reward window, block headers for the periods and a daily sample, the
week's coinbases read in the background at the lowest RPC priority). - mempool.space's layout: two columns in its order (reward stats, difficulty adjustment;
pools, hashrate; recent blocks, adjustments) under the block flow, our own panels beneath, and
View more » on four cards opening that card full screen — the whole week of pools as a
table, the year's chart large, the whole attributed window of blocks, a year of adjustments. - The pool chart is a labelled pie: every pool named beside its slice and joined to it by a
leader in the slice's colour, the small ones stacked, slices under half a percent gathered as
"Other" — and the "Other" threshold rises until every label fits the card, so no name runs off
the bottom at any window height. The pie and the year chart size themselves to the window. - Dollar figures on the reward stats while market polling is on, from a new
/api/pricethat
answers the cached spot price and never starts the exchange polling itself. - The Mempool space viewer left this page — it is on Overview, Block space and Mempool — and with
it the full-pool poll the page used to ask for. The reward window is exactly 144 blocks. - At a narrow window the block flow spans the width and is thinner, and the packages table scrolls
inside its card instead of bleeding into the neighbour; the three stacks become two below 1400px. - A streak of stale drops of the full-pool poll is one story: one warning event when it
starts, a counter on Node & RPC while it lasts, one event when the poll answers again with the
count and the span. On a day the node answered slowly for thirteen hours, each drop had been its
own event, 188 of the feed's 200 rows.
Every tab on one screen
Measured at 1093×945 and 1600×1000 with a dead-height probe, page by page:
- Globally: tighter card padding and heading spacing, and a short card beside a tall one keeps
its own height instead of being stretched to match (Chain & Sync's drill-down card had a
thousand pixels of nothing under its input; Node & RPC's poll cadence and connection cards
hundreds). - Overview: the block flow card a quarter shorter, and the Block space board capped to the
viewport (it set the whole right column's height), so the events are on the first screen. - Block space: the card tighter and the board sized so the whole page, note included, fits the
viewport without a scroll. - Markets: the board takes what the window leaves, and the exchange table and the order-book
depth sit side by side beneath it (under 1400px the table drops the pair, 24 h range and volume
columns; all still in the summary line and on the Kiosk) instead of the depth chart starting a
thousand pixels down. - Kiosk: tighter heads and gaps, the price panel's padding trimmed, the Block flow strip a
quarter shorter, and the full-screen button in that strip's head instead of floating over its
cards. - Mempool: keeps its two columns down to 900px (it stacked from 1100), and the room over goes
to its time-series charts, a fifth taller. - Explorer: a smaller title, the block strip's padding and scrollbar band trimmed, tighter
table rows and the table reaching the window's bottom edge — three more blocks on the first
screen. - Peers: a long onion or i2p address no longer makes the whole page scroll sideways (it clips
with an ellipsis and carries the full text as its title; a wide table scrolls inside its card),
rows and service badges tighter, the top strip's cards at their own heights. - Network: Throughput beside a stack of Upload and Traffic accounting, the throughput chart
taller, and the sources table taking the rest of the window. - Events: the feed reaches the window's bottom edge (it stopped a hundred pixels short).
- Chain & Sync: one-line notes under the block-size and tip charts (the longer sentences are
their tooltips), the transaction-rate figures as one wrapping line, values that keep to their
line, and a shorter uptime label — the first three rows sit level and the drill-down and indexes
reach the first screen. - Node & RPC: two stacks (RPC etiquette with its figures in two columns, Data quality and Node
connection; Poll cadence, self-telemetry beside the log tail, Panel sources and the RPC
console) instead of grid rows that left holes beside the short cards. - Admin: two stacks (Users over the audit log; Node actions over Change own password, whose
two fields share a row). Sign-in and 404: the same tighter card. - Display settings: rows, note and groups tighter, the sheet a little wider so hints wrap
less, and on t...
BlockYard 0.0.9
The initial release. Everything in it, like everything before it, was written by an AI directed by a human
operator, and audited by AI (docs/SECURITY-AUDIT.md, docs/SECURITY-AUDIT-2026-09-14.md). It is
experimental pre-release software. BlockYard runs on the machine that runs Bitcoin Core (25.0 or
later), because the explorer's address index is built from the node's own block files; a node on
another machine, and the experimental node that earlier measurements were taken on, are not supported.
862 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.
The 3D boards' effects, per board
- Each 3D board has its own effects list. The one Effects tab is now two: Space effects
(the Block space board's 28 switches — every effect but the two drawn on a price line) and
Market effects (the Markets board's 12: ripple, outline sweep, tide, cascade, twinkle,
scan line, fireworks, solar flare, wave, energy pulse, pipe bulge, ball lightning — the ones
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
all on / all off, which the no-repeat slider had silently taken away. A saved settings store is
migrated (schema 4): the Markets list starts as a copy of the list there was. - Each effects tab sets its board's cadence. Between effects, at least / at most
(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
(1.2 s by default, up to two minutes) on Space effects only — the candle board has no landing
— drive the scheduler's own timers, per board.
The no-repeat slider on each tab tops out at that list's length. - Nothing on the Markets board waits its turn. The pulse, the bulge and ball lightning were
held to a clock of 2.5-6 minutes between plays; they are ordinary picks now, and the list you
leave on decides how often any one of them comes round. - Everything on the Markets board moves along the hours — left or right, never toward the
viewer — and lights the candles or the line: fronts (outline, scan, tide, wave) run along the
chart, rings (ripple, shockwave, nova) start on the candle row, the light cycles ride in from
the two ends with their walls on the candle tops, the lightning ball rides the price line, and
ball lightning flies through the chart's own height. Candle faces light under an effect now
(the camera is low, and a glow painted on a candle's top alone was invisible). - Ball lightning strikes candles.
cellTopsbegan each tile at its own fractional x and y,
which on the candle board (tiles between grid lines) stored nothing, so every cell top was
zero and no arc found a target. Half the arcs now chain on from the struck block to
another in electric green, the instant they land, and half of those on to a third block. - The lightning ball is one pale gradient with a white-hot heart, and a pale burn behind it,
instead of five stacked discs of deepening blue that read as a dark blot.
The explorer's address history, from an index of our own
- Address pages show full history and balances on Bitcoin Core. This was the explorer's one
real gap, and it was not a bug: Core has no address index at any setting.getaddressbalance
andgetaddresstxidsare insight-style extensions carried by forks, and stock Core answers
Method not found(measured 2026-09-13 against two Core nodes). mempool.space
shows the same address's history only becauseelectrsbuilds that index from the block files
itself. So this monitor now does the same, in a form it can afford:server/chain/. - Reading the node's own files, not asking the node.
blk*.daton a current Core is
XOR-obfuscated at rest (-blocksxor, default since v28, the key inblocks/xor.dat) -- the
first spike concluded "not a Core datadir" and was wrong. De-obfuscated,server/chain/blockfile.js
frames the records;server/chain/tx.jsdecodes transactions and blocks, checked
field-for-field againstgetblock <hash> 3(91,813 spent coins in 16 sampled file pairs, 0
mismatches); and the undo (rev*.dat) records supply each spent coin's script and amount,
which is how the spending side is known without replaying the UTXO set. Pairing blocks with
undo records by trying every candidate was quadratic on the tiny early blocks (file 0: 35 minutes
and not finished); Core appends undo in connection order, so blocks are put in chain order by
their previous-block links and walked in step -- file 0 pairs in 5.9 s. - A lean row. One 21-byte row per (script, transaction that touched it): 8 bytes of
sha256(script), the height, the position in the block, and the net satoshis the transaction
moved for that script -- so a balance is a sum, with no node call. A script paid and spent in the
same transaction is one row (18% fewer). The full decoder builds Core's verbose shape and was 5.9
of the 9.5 single-core hours measured for the chain; the index needs only each output's value and
script bytes, soserver/chain/index/rows.jswalks the raw transaction itself and is checked
row-for-row against rows from the full decoder. - Flat sorted files, no database. Measured against
node:sqliteon three real file pairs
(docs/MEASUREMENTS.md§29): 21 bytes per row against 24-27, a 5.5 M rows/s sort against a
1.4 M rows/s key-ordered load, and no B-tree collapse once the keys outgrow memory. 256 sorted
segments by key prefix with a sparse index (one key per 4,096 rows) held in memory; a lookup
binary-searches the sparse keys and reads the one 86 KB block that can hold its key. - Built and measured (§30):
node scripts/index-build.js --out <dir> --workers 16read all
5,756 file pairs in 29 min 45 s (7.8 CPU-hours across 16 workers) and wrote
5,890,519,289 rows, 123.7 GB -- every height present exactly once, two stale blocks skipped,
and within 1.5% of the projection. 40 of 40 balances equalscantxoutsetat the same
height, to the satoshi; every (script, transaction) pair of four whole blocks from 2009 to the
tip found at its height and position with its amount. Lookups: 0.25 ms median first touch,
0.03 ms warm; a 2.3 M-transaction address's whole history summed for its balance in 83 ms.
For scale:scantxoutset, the only thing Core offers, took 26.5 s for one scan of 40 addresses
holding the node's RPC thread, and answers only the current balance, never a history. - It follows the chain.
server/chain/index/live.js, started by the server for each configured
index directory, polls every 30 s, fetches each new block withgetblock <hash> 3over RPC (so
the files are read once, at the build), and logs the rows before serving them in a
CRC-framedlive.logthat is replayed on restart and drops a record torn by a crash. A
reorganisation rolls the tail back to the fork; blocks 100 deep are folded into immutable
sorted layers and layers past 32 are merged; a reorganisation below what is folded stops the
follower and the page says to rebuild. Checked live: 12 blocks caught up in 6.5 s, 40 of 40
balances then equal toscantxoutset, and on release day the index reached a new block 16 s
after the node did. The rows it derives through RPC agree row-for-row with the rows built from
the files (test/chain-index-live.test.js). - The address page reads it.
addressIndexin a node's config names the directory (one index
serves every node on the same chain); the page shows the transaction count, balance, total
received and sent, and the transactions newest first with the net change each made, 25 a
page, deep pages costing no more than the first. The reply carriesindex.tip,index.behind,
index.followingandindex.stale, and the page says when the index is behind the node or has
stopped following. Checked live: every row of four pages, including page 4 of a 2.3
M-transaction address, matched the node's decoded transaction for txid, height and amount. - Without an index, the page is honest rather than empty. A node's refusal used to become
[],
thentxCount: 0, then "no transactions in this node's address index" -- a fabricated zero
indistinguishable from an unused address. The reply now carriesindexed: falsewith a null
count, the page says the index is absent, and the dead RPCs are not re-sent on every view: a
"method not found" is remembered per node for ten minutes, then asked again, because the daemon
behind a node id can change. - A rebuild in place is safe.
buildIndexempties its output directory first, so a follower's
live.logand layers -- holding the reorganised-away blocks -- cannot survive into a new index and
a fresh follower catches up on the chain as it is (test/chain-index-live.test.js). Stop the
server, run the same command, start it; the advice to build into a fresh directory is withdrawn. - A missing address index is built by BlockYard itself, in the background (operator: "Is it
possible to run step 6 in the background, and have a status notification in blockyard when the
index process is finished?"). On start, anaddressIndexdirectory with no index in it is built
on worker threads while every page keeps serving; the progress is a quality flag on the Overview
and a line on the address page (phase, files done, rows so far, time left); an event marks the
start, the finish and a failure, and the browser toasts it; when it finishes the follower starts
and address pages work with no restart.addressIndexBuild: "manual"keeps it from happening.
The installer's step 6 offers background (the default), here, or later. - The build shares the machine with the node, and behaves like it. Every...