blockyard 0.0.9 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +359 -1
  2. package/README.md +48 -27
  3. package/SECURITY.md +2 -2
  4. package/bin/blockyard.js +2 -1
  5. package/docs/API.md +17 -15
  6. package/docs/ARCHITECTURE.md +128 -10
  7. package/docs/CONFIGURATION.md +39 -30
  8. package/docs/DEFECTS.md +4 -1
  9. package/docs/GETTING-STARTED.md +18 -8
  10. package/docs/INSTALL.md +97 -37
  11. package/docs/MEASUREMENTS.md +147 -0
  12. package/docs/PLAN-SCORCHED-YARD.md +456 -0
  13. package/docs/PLAN-SKIES.md +142 -0
  14. package/docs/SECURITY-AUDIT-2026-09-16.md +647 -0
  15. package/docs/SECURITY.md +58 -22
  16. package/docs/TROUBLESHOOTING.md +44 -5
  17. package/docs/USER-GUIDE.md +505 -35
  18. package/package.json +4 -2
  19. package/public/404.html +1 -1
  20. package/public/css/app.css +393 -82
  21. package/public/donate-qr.png +0 -0
  22. package/public/index.html +353 -109
  23. package/public/js/agents.js +228 -51
  24. package/public/js/app.js +131 -16
  25. package/public/js/blockanoid.js +15 -7
  26. package/public/js/blockout.js +15 -7
  27. package/public/js/blockscene3d.js +230 -38
  28. package/public/js/charts.js +21 -21
  29. package/public/js/depthchart.js +31 -27
  30. package/public/js/details3d.js +1481 -73
  31. package/public/js/doom.js +31 -0
  32. package/public/js/dosaudio.js +48 -0
  33. package/public/js/dosgame.js +389 -0
  34. package/public/js/dosio.js +186 -0
  35. package/public/js/dospc.js +1353 -0
  36. package/public/js/dosworker.js +196 -0
  37. package/public/js/explorer.js +7 -1
  38. package/public/js/livingsky.js +494 -0
  39. package/public/js/login.js +8 -2
  40. package/public/js/markets.js +46 -8
  41. package/public/js/mining.js +314 -36
  42. package/public/js/panels.js +41 -28
  43. package/public/js/pricechart.js +14 -13
  44. package/public/js/quake.js +20 -0
  45. package/public/js/safenext.js +14 -0
  46. package/public/js/scorched.js +1051 -0
  47. package/public/js/scorchedai.js +227 -0
  48. package/public/js/scorchedair.js +286 -0
  49. package/public/js/scorchedfx.js +376 -0
  50. package/public/js/scorchedshop.js +105 -0
  51. package/public/js/scorchedwind.js +69 -0
  52. package/public/js/scorchedyard.js +1338 -0
  53. package/public/js/settings.js +368 -100
  54. package/public/js/soundcard.js +459 -0
  55. package/public/js/tetrust.js +15 -6
  56. package/public/js/tetsound.js +35 -5
  57. package/public/js/theme.js +235 -0
  58. package/public/js/wolf3d.js +22 -0
  59. package/public/js/x86.js +1978 -0
  60. package/scripts/check.js +46 -0
  61. package/scripts/donate-qr.py +12 -9
  62. package/scripts/dos-bench.js +56 -0
  63. package/scripts/index-build.js +9 -2
  64. package/scripts/pool-map.js +152 -36
  65. package/scripts/setup.js +142 -22
  66. package/scripts/shots.mjs +27 -0
  67. package/scripts/smoke.sh +7 -6
  68. package/scripts/tls.js +31 -0
  69. package/scripts/ui.js +4 -2
  70. package/server/auth/sessions.js +33 -13
  71. package/server/chain/blockfile.js +64 -5
  72. package/server/chain/index/build.js +451 -58
  73. package/server/chain/index/heights.js +29 -3
  74. package/server/chain/index/live.js +13 -7
  75. package/server/chain/index/rows.js +6 -1
  76. package/server/chain/index/store.js +28 -5
  77. package/server/chain/index/worker.js +23 -11
  78. package/server/collect/logparse.js +65 -18
  79. package/server/collect/markets.js +76 -7
  80. package/server/collect/mining.js +32 -0
  81. package/server/collect/monitor.js +54 -12
  82. package/server/collect/network.js +305 -0
  83. package/server/config.js +53 -22
  84. package/server/http/api.js +119 -18
  85. package/server/http/games.js +77 -0
  86. package/server/http/server.js +30 -5
  87. package/server/http/sse.js +53 -7
  88. package/server/main.js +66 -11
  89. package/server/rpc/allowlist.js +26 -0
  90. package/server/rpc/client.js +30 -2
  91. package/server/store/audit.js +6 -1
  92. package/server/store/history.js +19 -3
  93. package/server/store/ledger.js +15 -4
  94. package/server/tls/selfsigned.js +160 -0
  95. package/systemd/blockyard.service +41 -8
  96. package/docs/PRIVATE-LEADERBOARD.md +0 -230
  97. package/docs/STATE-2026-09-09.md +0 -200
package/CHANGELOG.md CHANGED
@@ -6,7 +6,364 @@ All notable changes to this project are documented here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
- Nothing yet.
9
+ ## [0.1.1] — 2026-09-17
10
+
11
+ A day of hardening, and a new game. The headline is the **third AI security audit**, run on
12
+ 2026-09-16 over the whole codebase by four auditors in parallel, and every one of its findings
13
+ fixed the same day with a test that holds it: one high (a client that opened the live stream and
14
+ stopped reading was never dropped, so memory grew without bound), eight medium, seventeen low and
15
+ six informational. The **address index** learned to resume an interrupted build instead of starting
16
+ its hours-long read again, and is now checked in the suite against a second, deliberately naive
17
+ implementation; property tests over Bitcoin Core's file formats found and fixed three flaws in
18
+ reading undo data. The **skies** were made clear: every board picks the Galaxy or the Earth sky in
19
+ one place, and the Earth sky gained a warm sun with a halo, a moon that glows through its real
20
+ phases, and softer clouds. And under Diversions, **Scorched Yard**: Scorched Earth on the block
21
+ engine, with the manual's weapons and computer players, up to eight tanks, a cheat mode that draws
22
+ the firing solution, and wind that is a real fluid simulation flowing over the hills. The games now
23
+ load only when their page is first opened. 1,068 tests, from 945.
24
+
25
+ ### Upgrading from 0.1.0
26
+
27
+ - **Node.js 22.2 or newer** is required (was 22.0): the resumable index build uses Node's built-in
28
+ CRC-32.
29
+ - **With accounts off, the node connection form works only from the monitor's own machine**
30
+ (`127.0.0.1`). `auth.openNodeConfigFromNetwork: true` restores the old reach; with accounts on,
31
+ an admin can use it from anywhere, as before.
32
+ - **Wallet RPCs are refused** in the RPC console, reads included (`getbalance`, `listunspent`, …).
33
+ - **The shipped systemd unit is sandboxed.** If you install it again, edit its `ReadWritePaths=`
34
+ lines to your install's `data` and `config` directories, and add your address index directory.
35
+ - **The address index build refuses a directory** that holds anything an index does not write, a
36
+ symlink, or a home, working or blocks directory. Point `addressIndex` at a new or empty
37
+ directory, or at an existing index.
38
+ - **Sky settings are migrated** from the old global sky and per-board star switches to one Sky
39
+ choice per board; each board keeps what it drew.
40
+ - **`scripts/pool-map.js`** asks before replacing the pool map (`--yes` to skip), and
41
+ **`scripts/setup.js`** takes the RPC password from `--rpc-password-file` or stdin; the plain
42
+ `--rpc-password` still works, with a warning.
43
+
44
+ ### Security
45
+
46
+ - **The third audit's low and informational findings, fixed the same day** (`docs/SECURITY-AUDIT-2026-09-16.md`, tests in `test/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; `--workers` is validated; malformed self-written files are cut or skipped rather than crashing. *Scripts and supply chain*: `setup.js` no longer echoes the RPC password and takes it from a file or stdin; `pool-map.js` refuses 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.md` names 0.1.x as supported, and the smoke script's open-access instance now really opens access
47
+ - **The third audit's high and medium findings, fixed the same day** (`docs/SECURITY-AUDIT-2026-09-16.md`, tests in `test/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.openNodeConfigFromNetwork` restores the old reach. A save that moves the node to another host drops the old endpoint's `rpcUser`, `rpcPassword` and `cookieFile`, 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=strict` with `ReadWritePaths` for `data/` and `config/`, `ProtectHome=read-only`, no capabilities, a system-call filter, `UMask=0077`), and the app was started and exercised under exactly those settings
48
+ - **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 admits `listdescriptors` and `gethdkeys`, 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
49
+
50
+ ### Address index
51
+
52
+ - **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
53
+ - **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
54
+ - **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 and a restart, and asserts a reorganisation into folded layers is refused as stale. It found no discrepancy; it fails on each of five deliberately introduced faults. About three seconds
55
+
56
+ ### Performance
57
+
58
+ - **Games load when first opened.** The seven games (Tetrust, Blockout, Blockanoid, Scorched Yard and the three DOS ports) were imported by the app shell at startup, so every page paid for all of them. They are now fetched the first time their page is opened: the modules loaded at startup drop from 39 to 21, and from 1.42 MB to 1.09 MB. A failed load shows a message and is retried on the next visit. A test holds the shell's static imports free of any game module
59
+
60
+ ### Skies and lighting
61
+
62
+ - **The moon glows, and its phases can be watched.** The same two-layer halo the sun has, in moonlight silver, stronger the fuller the moon. The phase has always followed the real calendar (full on the real full-moon dates, new on the new ones, the quarters between); under the cycle clock, where a day is 24 minutes, the calendar now runs a day per cycle day, so a whole month of phases goes by in about twelve hours on the wall. The disc is a little larger
63
+ - **The sun has a halo, and no rings.** Its soft edge was twelve steps, which on a big panel is several pixels a step and showed as concentric rings; the ring counts are left to the fill's own rule now, about one a pixel. And a halo in front of the clouds, a wide faint corona and a tighter bright one, so the sun sits in its own light rather than being a coin on the sky
64
+ - **The Earth sky's cirrus is gas, not a chain of discs.** Each high wisp was a row of hard-edged flat discs fourteen pixels apart, and where they overlapped the outlines stacked into a visible row of rings. A wisp is a scatter of soft-edged puffs now, built the way the cumulus is, jittered off the line and stretched along it, so nothing in it has an edge to band
65
+ - **The Earth sky's sun and moon are there to be seen.** The sun is half again as big and its disc is drawn in front of the clouds (its glow still behind them), so it is no longer a pale spot under a cumulus at noon; the moon likewise. A board can say where its horizon is — Scorched Yard's land fills the lower third, so its sun sets behind the hills instead of under them. And a **Moon** choice under the Earth: up every night, highest at midnight and never thinner than a fat crescent (the shipped choice), or its real track and phase, which some nights means no moon at all
66
+ - **One sky per board: the Galaxy or the Earth** (docs/PLAN-SKIES.md; operator: "The skybox settings are not clear in the preferences, for which sky settings apply to which panel"). There were two skies described from two directions and no board named anywhere: one global Space-or-Living switch for every board at once, six scattered "Star field" toggles that under the living sky meant "draw the day or draw nothing", and four private galaxy switches beside the Sky tab's own. Now: the Sky tab opens with a table of every board that has a sky and the one it draws — Block space (and the Kiosk's left panel), Markets & Price (and its right), Tetrust, Blockout, Blockanoid, Scorched Yard — each a select for **Galaxy**, **Earth** or **None**; below it, the two skies with only their own controls, never dimmed. Each board's tab carries the same one **Sky** choice in place of its old star and galaxy toggles, and the games' panels have one **sky** button round the three. The star-field sky is named the **Galaxy** (not Space, which is Block space's word), its arms toggle is **Spiral arms** and its placement **Arms centre**; the living sky is the **Earth**. Scorched Yard ships under the Earth, everything else under the Galaxy. A stored setting comes forward drawing exactly what it drew: stars off becomes none, stars on becomes whichever sky the old global switch named; the step is idempotent, because server-stored settings carry no version and re-run it on every boot
67
+ - **A board can ask for a harder lamp.** The renderer put every face of every cube between 0.34 and 0.86 of its own colour with the top at 0.8 — a narrow band, which reads as tinted rather than lit. `lightGain` stretches that range about its middle and `topLight` sets where the tops sit; 1 and 0.8 are exactly what every board drew before. Scorched Yard's field uses a hard lamp at the viewer, so the front of the land carries the light and the strata separate
68
+ - **The sun is not white.** The Living sky's sun ran to a near-white disc at noon; it is deep orange on the horizon and warm gold overhead now, which is what a sun looks like to an eye rather than to a camera
69
+ - **The Living sky no longer depends on the Star field switch.** With the Living sky chosen and that switch off, every board went black: a control about stars was hiding the whole day. A board that asks for the Living sky now draws it whatever the star switch says, and the star field still comes out at night by itself
70
+ - **The deep sky belongs to the star field.** The spiral galaxy, nebulae, dust lanes, halo clusters and distant galaxies are off while the Living sky is drawing, the way the Space effects already are, and their rows in Display settings → Sky are dimmed and say "Star field only" (the Living sky's own rows dim the same way under Space). Nothing is forgotten: choosing Space brings them all back as saved. Scorched Yard's panel hides its star and galaxy switches under the Living sky for the same reason
71
+ - **The Block space lamp hangs in six places at three heights**: straight above, top left, top right, bottom left, bottom right, or at the viewer, each low, middle or high (Display settings → Block space → Light and Light height). A saved upper-left, upper-right or front comes forward as top left, top right and the viewer
72
+ - **The Living sky**: a second sky type (Display settings → Sky → Sky: Living sky) for every board that draws a sky — a real day from this machine's clock. The sun rises on the left and sets on the right, the dome passes through night, dawn, day and dusk by the sun's altitude, a twilight glow stands at the sun's side, clouds drift lit from the sun (cumulus in clusters of soft discs, cirrus above them), the moon comes up at tonight's phase with its terminator, and the star field fades in as the sun goes down. A clock setting (real time, a day every 24 minutes, a fixed hour), weather (clear, scattered, overcast, storm with rain and lightning), cloud cover, an optional latitude for real sunrise and season, and switches for crepuscular rays, a rainbow and shooting stars. Drawn under the same canvas rules as everything else (no gradients or blend modes: the dome is one huge soft disc cached until the sun moves half a degree). The Space effects are off while the Living sky is on, their switches kept
73
+
74
+ ### Scorched Yard
75
+
76
+ - **Scorched Yard: the rest of the control plan.** *Correcting*: click an enemy tank to mark it; after your shot the panel says how it did against it (*10 over*, *6 short*, *close*, *hit*) and **C** sets the power that miss implies, from your own last shot. *Weapons*: **W** opens a grid of what you own, biggest blast first, with counts and keys; **1–9** pick in the shop's order; **Q** picks your last weapon; the item pills are buttons. *The aim guide*: a ghost of the first fifth of the flight while you aim, or the whole flight, or off. *The fire button* names what it will send, and the last of a weapon sold one at a time asks once before it goes. A press on your own tank turns the barrel without touching the power. The keys are a table. Three new settings: Aim guide, Correction helper, Confirm the last of a weapon
77
+ - **Scorched Yard: the old wind code is gone.** The four earlier wind pictures (marching particles, dashed currents, a plasma wash, bowing bands), the smoke dye the fluid first carried, and the air clock only they used are deleted, with the tests that covered only them: the wind module drops from about 400 lines to 69, and the air module loses its dye grid. What is drawn is unchanged — the simulated air, its flow lines, and the sky's faint ripple
78
+ - **Fixed: Scorched Yard's computer players seemed to aim at tanks already destroyed.** A computer player chose its shot only when its thinking pause ran out, so for the whole pause its gauge (and, in cheat mode, its trajectory) still showed its previous shot, usually aimed at the tank it had just destroyed. It now plans the moment its turn begins, against the tanks alive then, and its barrel and power ease from the old aim to the new one while it thinks. Cheat mode draws only your own firing solution. And the random shot that the Moron fires, and that the Shooter and Poolshark fall back on, heads toward a living enemy instead of the middle of the field, which had kept shelling the empty half once the tanks there were gone: in 4,357 simulated shots none now fires away from every tank still standing, against 20 before
79
+ - **Fixed: Scorched Yard froze when the land was blown up.** Measured on a nuke in the browser: frames of 150 ms, and 136 frames over 33 ms in seven seconds. Four causes, each measured and fixed. The simulated air re-ran six seconds of warm-up and read the land canvas back from the GPU on every crater; it now does that only for a new round's land, and a crater moves the air's floor from the rules' own column heights. The land's redraw used the renderer's general paint sort, which builds an outline for every cube and tests pairs for overlap, 43 ms for 1,800 resting cubes twice a blast; resting land now draws in a known order (back rows first, columns outside in), pixel-identical in a full-canvas diff and 7 ms. The flow lines were 5,500 separate round-capped segments a frame; they are continuous paths at half the points. And the wind layer redrew at sixty frames a second during every shot; it is held to thirty, and the effects' soft fills name a modest ring count rather than one per pixel of radius. The same nuke now runs with a worst frame of 33 ms, like the idle game
80
+ - **Scorched Yard: the flow lines fade out at the edges instead of vanishing.** A tracer was removed the moment it crossed the field's edge, so its whole line disappeared at once. Each line segment now fades by how near it is to the left, right or top edge, and a tracer runs on past the boundary, out of sight and already faded, before it is reborn
81
+ - **Scorched Yard: the simulated air is shown as streaklines, not smoke.** Any soft density drifting across the sky reads as smoke however it is tuned. The picture is now the wind-map kind: 320 weightless tracers carried by the simulated velocity, each drawn as a thin line along its recent path, so the lines curve where the air curves — up over a hill, round the eddy behind it. They taper to nothing at both ends with no bright head, and the whole field is four strokes, about a millisecond and a half to draw. The simulation's smoke is no longer computed in the game, which also brings its per-frame cost back to under half a millisecond
82
+ - **Scorched Yard: the air is an actual fluid simulation.** Every earlier wind effect was kinematic, sprites and finally sine curves told where to be at a given moment, so nothing in them could flow round anything. The wind layer now runs a small Stable Fluids solver (96 by 48 cells, about half a millisecond a step): the wind drives the air toward its speed and direction, the land is the solid floor so the air rises over the hills and rolls into eddies behind them, vorticity confinement keeps the eddies alive, and streams of smoke breathed in on the upwind edge are carried, bent and pulled apart by that flow. A new round's land gets eight simulated seconds of warm-up, so the air is already flowing when it is seen; a change of wind turns the air round by its own momentum. The smoke's opacity is compressed so thin smoke downstream stays visible and the dense streams at the edge do not dominate, and it reads stronger against a bright sky than a dark one. The bowing bands are gone; the faint refraction of the sky underneath stays
83
+ - **Scorched Yard: twice the bands of air, and they hold up in daylight.** Ten bowing bands instead of five. Pale air on a pale blue sky has a fraction of the contrast it has on black, so the bands sample how bright the sky behind them is every second and a half and carry up to about three times more at noon, and no more than before at night
84
+ - **Scorched Yard: the wind is seen by what it does to the sky.** Every streak had a bright head and a fading tail, which is the shape of a meteor, so on a starry sky the wind read as shooting stars. The streaks and the dashed currents are gone. The sky behind the land now refracts: it is copied across the wind plane in narrow columns, each lifted or lowered by a wave that rolls downwind at the wind's speed, so the stars, the moon and the clouds sway a few pixels as if seen through moving air; the land in front is untouched. Over it, a handful of broad, faint bands of air bow as they cross, with no head and no point anywhere in them. Both follow a signed distance the air has travelled, so they only move downwind and bend round through zero when the wind turns, and neither draws anything in still air
85
+ - **Scorched Yard: the air at half the count, tuned for night as well as day.** Each particle is a little brighter and thicker with a longer tail, so the wind still reads at a glance with half the particles it had; the currents are few, thin and faint, since on a dark sky at a strong wind they were a wall of dashes
86
+ - **Scorched Yard: half the air particles.** The wind layer runs half as many particles as it did (at most 210 on a wide panel, down from 420); the tails and the currents carry the motion, so the picture keeps its flow at half the per-frame cost
87
+ - **Scorched Yard: every one of the twenty-seven shells that fly looks like itself.** Every projectile was the same white dot. Each weapon now has its own entry: its core colour and size, a glow in its colour, a trail in its manner at its own length, count, width and colour, and a ring for the heavy ones. Within a family the baby is small and pale, the plain one middling, the heavy one big, dark and ringed, so a missile is not a baby missile and a Death's Head is not a MIRV. The manners: a missile's exhaust, a nuke's pulsing green glow and falling sparks, a comet tail for the MIRV, the Leap Frog and the Death's Head, a Funky Bomb's rainbow (its bomblets spin it faster), a tracer's thin grey dots and no glow, a smoke tracer's grey puffs, sparks behind the rollers, pale wisps behind the riot bombs, shed clods behind the dirt shells and droplets behind liquid dirt, flickering fire and dripping embers on napalm and a blue-white flame on hot napalm, spinning arms of glint on the diggers and sandhogs
88
+ - **Scorched Yard's land is brighter.** The tanks and the land go through the same lamp; the tanks looked lit because their paint is bright. Each stratum is lifted a fifth to a quarter in value at the same saturation, so the front of the land sits where the front of a tank does
89
+ - **Scorched Yard: the mouse drag clicks.** The keys and the wheel clicked as the aim moved; dragging on the field was silent. It clicks on every whole degree and every ten of power it crosses, no more than every 45 ms, so a slow drag ticks and a fast one whirs
90
+ - **Fixed: Scorched Yard's panel showed the wrong tank's cash while you shopped.** A purchase always took the money; the "This turn" panel was showing the tank whose turn it was when the round ended, and when that was a computer player its cash sat there unmoved while yours went down in the shop's own header. Between rounds the panel shows the human, marked "shopping"
91
+ - **Scorched Yard: vibrant strata, and a lamp that no longer bleaches them.** The land's five strata were dust-and-shadow browns that read as tinted whatever the lamp did; they are the same strata at full saturation now — hot magma, slate rock with some blue in it, terracotta clay, golden soil, a green that is green. The lamp gain comes down from 2.15 to 1.7: the front face sits at about 1.1 of its own colour and the face turned away under 0.2, where the higher gain had pushed the front past the point a channel clips and a saturated colour goes pale, which was the wash that kept being reported
92
+ - **Fixed: tanks in the air and in the ground after a new Scorched Yard game.** The land canvas was redrawn only when the land's version number moved, and a fresh game restarts that count where the last one began, so a new game after changing the opponent count (or a restart before a shot) placed the new tanks over the old terrain. The cache is cleared on every new game and every new round. The placement itself was always right: a test now stands three, five and eight seats on their ground over forty seeds each
93
+ - **Scorched Yard: the air is a steady flow with currents.** Reborn particles used to re-enter at the upwind edge, so in a light wind the field was dense there and empty downwind, and the density itself read as a front rolling in; a particle that runs out of life is reborn anywhere now, and only one that leaves the field comes back at the edge. Each particle carries a tail, drawn as a ribbon that thins and fades, so what moves reads as carried rather than as dots. And the currents themselves are drawn: a few dozen streamlines traced through the same field, bending round the same eddies, animated by a dash offset that runs downwind at the wind's speed
94
+ - **Fixed: Scorched Yard's air rushed backwards through a change of wind.** The wind layer's wash and its eddies took the page's clock and multiplied it by a rate that depended on the wind, so on a page a day old the smallest change in the wind moved them by a day's worth of travel, and flipped them end to end when the direction turned. The air keeps its own clock now, integrated a frame at a time at the current wind's rate: a change of wind changes how fast it runs from there on, and nothing else. And the field no longer scrolls its two octaves against each other vertically, which read as motion against the wind: it morphs in place, and the wind's drift is the only travel there is
95
+ - **Scorched Yard: eight seats.** Computer players go up to seven, for the original's eight-tank table; two is still the shipped game. Eight distinct tank colours, and the field seats eight without overlap
96
+ - **Scorched Yard: cheat mode.** A switch on the panel and in Display settings draws the firing solution while you aim: the shell's own path under this round's wind and the game's gravity, clipped where it would meet the dirt, with a ring where it lands. It is recomputed every frame the aim moves, so the arc bends live under the wind. It is the rules' own trajectory, so what it draws is what the shot does
97
+ - **Scorched Yard: the air is a flow field.** The wind layer is no longer dashes told where to be at time t: it is particles advected through the curl of a drifting noise field, over a faint plasma wash of the same flow, so what moves is material being carried, bent by eddies on the way. The swirl is capped at a third of the base wind, so every particle nets downwind and no streak ever points upwind. A change of wind bends the flow into the new speed and direction over half a second instead of swapping layers
98
+ - **Scorched Yard: the wind's strength changes every turn, its direction once a round.** Holding both for a whole round made the wind something you read once and forgot; holding neither was the flip-flop that started this. The round draws a direction and keeps it, and every turn draws a fresh strength within it
99
+ - **Scorched Yard: the air runs at one steady speed.** The wind layer carried a gust — a slow wave that lengthened and quickened every streak together by a third either way. It never reversed (measured frame by frame, no streak ever moves upwind) but a stream that surges and eases while the gauge reads one number does not look like that number. The gust is gone: the streaks advance at exactly the rate the gauge says
100
+ - **Scorched Yard: a restart button.** A war is five rounds, and a player knocked out in round one had nothing to do but watch the computer finish it. **Restart** on the panel, or **F2**, starts a fresh war from round one at any moment, and a knocked-out player is told so
101
+ - **Scorched Yard: the wind changes by crossfade, and says which way it blows.** A new wind used to replace the old one between two frames, so every streak jumped. The old air thins away while the new air comes up over it, already at its own speed and in its own direction, over about half a second; the new one leads, so the direction is readable long before the old one is gone. A row of chevrons at the top of the field points downwind, as many as the wind is strong, brightest just after a change
102
+ - **Scorched Yard's wind holds for a round.** A new wind mode, **Once a round**, is how the game ships: the wind is drawn when the round starts and keeps its direction and its strength until the round ends, so the air blows one way while you read it and every shot in the round is judged against the same gauge. The original's every-turn wind is still there under Display settings → Scorched Yard → Wind, and it can still turn right around between two shots
103
+ - **Fixed: nothing could be bought in Scorched Yard's shop.** Every panel decided whether to redraw by comparing its new markup with `box.innerHTML`, which a browser hands back normalised, so the comparison never matched and the markup was rebuilt on every draw. That was harmless while the board only drew on events and fatal once the wind kept it drawing thirty times a second: a button replaced between the press and the release is never clicked. Each panel remembers what it wrote, and the shop keeps its scroll position when something is bought
104
+ - **Scorched Yard's controls, the first step of the rescoped plan.** A held arrow accelerates (one a step, then two, then five, so the whole arc is about a second and a half and the last degree is still one press); Shift is fine and Ctrl coarse, and the keys line says so; **, and .** nudge the power by one for bracketing; the **wheel** over the field changes power, and with Shift the angle; **click the angle or the power** on the panel and type it; **R** fires the last shot again
105
+ - **Scorched Yard's explosions are painted, not built out of tiles.** The renderer already knew how to draw a firework — a white core, a shockwave, sparks that curve and trail and twinkle, and smoke built from flat discs that add up to a soft cloud — and a shell landing now draws with the same machinery, through the board's own projection, so the fire is exactly over the crater it made. A tank going up takes its own colour, and the dirt's dust is a low puff. `details3d` gained one seam for it: a board may hand the renderer an `overlay` to paint after the cubes, with the projector and `softStops`
106
+ - **Scorched Yard: the aim gauge.** A protractor stands over the tank whose turn it is — ticks every fifteen degrees, the round numbers written in, a needle along the current angle whose length is the power, and the angle and power printed at its tip. It dims for a computer player's turn, so you can see what it is about to do
107
+ - **Scorched Yard is lit from the viewer.** The field was lit from straight overhead, which lit the tops of the cubes and left every face you actually look at on one flat shade. The lamp stands low and at the viewer now, so the front of the board carries the light and the strata separate
108
+ - **Scorched Yard, the wind is its own flat layer.** It used to be tiles in the 3D field: they bobbed as they crossed, they were drawn over the hills they should have passed behind, and a gust that eased off dragged them backwards (the wind appeared to change direction while you were thinking). The air is now a plain 2D plane between the sky and the land — level streaks whose length is the wind's speed, in three bands of depth, the land in front of them hiding whatever goes behind a hill. It keeps blowing while the board is idle, at its own rate, and only ever blows one way
109
+ - **Scorched Yard, the blast is made of blocks.** The fireball, the shockwave, the embers and the smoke were spheres, and the engine gives a sphere a rim and a glint, so an explosion was a heap of bubbles on the dirt. They are blocks now, like the land, the tanks and the shells: a fireball on the field's own grid, hot at the centre and dark at the rim, a ring racing out, embers thrown on ballistic paths, and smoke that rises, spreads, drifts downwind and thins by its alpha rather than shrinking away
110
+ - **Scorched Yard, attract mode** (M5): a **watch** switch on the game's panel (and Display settings → Scorched Yard → Attract mode) hands your chair to another computer player — every seat is a computer player, a round rolls into the next without waiting at the shop, and when one of them wins the war a fresh one begins. Nothing is written to the high-score table for a war nobody played
111
+ - **Scorched Yard**: the field runs to the frame's edges and its bottom (no sky under or beside the land); the wind is visible — motes of dust drift across the field at its speed and in its direction, a pennant on every turret streams downwind and lifts with the strength, and under the Living sky the clouds drift with the round's wind
112
+ - **Scorched Yard, the look** (M4): a shockwave ring and smoke that rises, grows and drifts on the wind after every blast; a tank's death as sparks in its colour and three pieces of hull tumbling; dust where fallen dirt lands; a tank's fall animated, by gravity or slowly under a drawn parachute; tracks under the hull; the tanks talk ("Nuke 'em!", "Ouch!", "Tell my wife…") in a bubble over the field; a march in D minor as the game's music; under the Living sky each round draws its own hour; music, talk and per-round-sky switches; a screenshot in the guide
113
+ - **Scorched Yard, the computer players** (M3): the manual's eight personalities — Moron, Shooter (straight shots), Poolshark (bank shots off rubber walls), Tosser (lobs, correcting by halves), Chooser (the best of the three), Spoiler (nearly perfect, from the wind and gravity), Cyborg (a Spoiler with a grudge for whoever hit it, the weakest, the leader), Unknown (drawn each round) — planning with a side-effect-free shot simulator that carries the walls, the dirt and the tanks; they use batteries and shields before firing and shop to their tastes; a setting fields a mix or one kind
114
+ - **Scorched Yard, the roster and the shop** (M2): the manual's thirty-three weapons at the manual's prices, packs and blast radii (SCORCH.DOC) with the original's behaviours — the four blasts, Leap Frog's three growing warheads, Funky Bomb, MIRV and the nine-head Death's Head, the tracers, three rollers, the riot charge and blast as wedges cut from the turret, the riot bombs, the dirt shells, liquid dirt that oozes and fills, the dirt charge, the earth disrupter, napalm that flows and burns, the diggers and sandhogs that bore, plasma thrown from the tank, the laser — and eleven accessories (Shield, Force and Heavy shields, Mag Deflector and Super Mag, parachute, battery, auto defense, fuel, contact trigger, heat guidance); the manual's six wall modes with none as the default; several shells in flight at once; cash for damage, kills and survival, interest between rounds, and the shop in the round-end overlay; the items line and cash on the HUD; keys to drive, heal, shield and arm; the computer players shop too; fire and beams on screen
115
+ - **Scorched Yard, the first cut** (docs/PLAN-SCORCHED-YARD.md, M1): Scorched Earth on the block engine, under Diversions. A 96×48 field of dirt cubes drawn as runs by stratum, tanks with a turning barrel, a shell that flies under gravity and wind with the original's angle and power, craters, dirt that falls and settles, fall damage, death and its blast, the four wall modes, four missiles, one human against two computer players (the Moron; more opponents and personalities to come), turns, a game of rounds, a HUD with the wind and every tank's health, the mouse to aim, a high-score table, and a settings group for the rules
116
+
117
+ ## [0.1.0] — 2026-09-16
118
+
119
+ The first minor release, two days after the initial 0.0.9, and everything since it in one place:
120
+ the 0.0.10 that was prepared on 2026-09-15 was never published, and its changes ship here. Four
121
+ things shaped it. The **first outside review** of 0.0.9 on bitcointalk, which called out a monitor
122
+ that bound every interface with no sign-in: 0.1.0 ships hardened — this machine only, sign-in on,
123
+ HTTPS with a certificate it makes itself, and no outbound connection at all until the exchange
124
+ feed is switched on. A day of **use on a laptop screen**, which found every tab a scroll: every
125
+ page now packs to one screen at 1093×945, and the Mining tab follows mempool.space's dashboard
126
+ with its **View more** panels. **Appearance**: the layout's colours are a setting — light or
127
+ dark, five themes, or nine colours of your own. And the **DOS Diversions**: Wolfenstein 3D, DOOM
128
+ and Quake, unmodified shareware on a PC emulated in the browser, written here with no
129
+ dependencies, because "can it run DOOM?" deserved a straight answer. 945 tests, from 862.
130
+
131
+ Upgrading from 0.0.9: [docs/INSTALL.md §11](docs/INSTALL.md#11-updating) — the first start after
132
+ the update serves HTTPS, creates the `admin` account and prints its password once.
133
+
134
+ ### Security
135
+
136
+ - **Hardened defaults** (after the first outside review of 0.0.9): the monitor binds `127.0.0.1`
137
+ and requires sign-in out of the box — the first start creates the `admin` account and prints its
138
+ password once (or takes `BLOCKYARD_ADMIN_PASSWORD`). Reach it from elsewhere over an SSH tunnel,
139
+ or bind a LAN address with `BLOCKYARD_BIND` / `server.hosts`; open read-only access is a choice
140
+ (`BLOCKYARD_AUTH=0`), announced at boot. The boot banner says how to reach a loopback-only
141
+ monitor. Existing installs keep whatever `config/local.json` says.
142
+ - **HTTPS by default.** Every listener serves HTTPS; with no certificate named, the first start
143
+ makes the monitor its own self-signed one under `<data>/tls/` — written without dependencies
144
+ (`server/tls/selfsigned.js`: a v3 X.509 certificate assembled in DER and signed with ECDSA P-256
145
+ over SHA-256), naming the bound hosts, this machine's addresses and hostname — and remakes it
146
+ near expiry or when a bound address is missing from it. `blockyard tls` remakes it by hand
147
+ (`--san` to add names, `--print` to export it). Your own certificate still replaces it;
148
+ `BLOCKYARD_TLS=0` serves plain HTTP behind a proxy. The installer's summary and port probe know
149
+ both schemes.
150
+ - **The installer asks twice before a non-loopback bind.** A LAN or wildcard address is written
151
+ only when the operator types it and confirms it; the default it offers is `127.0.0.1`.
152
+ - **Market polling is off by default** — a new checkbox, **Display settings → Markets & Price →
153
+ Enable market polling**, shared by every screen. A fresh install makes no outbound connection
154
+ but to the node: the Markets and Kiosk tabs say that polling is off and where the switch is,
155
+ Overview's price line stays hidden and the explorer shows no dollar figures. Ticking it takes
156
+ effect at once; unticking parks the feed at once. `BLOCKYARD_MARKETS=0` still removes the feed
157
+ so that the checkbox cannot turn it on. Existing installs tick the box once.
158
+
159
+ ### Appearance
160
+
161
+ - **A first tab in Display settings for the colours of the layout.** A theme mode — Light, Dark,
162
+ or System, which follows the operating system and changes with it — and theme cards with swatch
163
+ strips of the face the mode picks: **BlockYard** (the shipped look, the default; its dark face
164
+ is value for value what the monitor drew before), **Mono** (clean grayscale), **Nous** (GitHub's
165
+ chrome with a Nous blue accent), **GitHub** (Light Default and Dark Default) and **Catppuccin**
166
+ (Latte and Mocha). **Custom** is nine colour pickers — page, panels, text, muted text, accent,
167
+ lines, good, warning, bad — with the rest of a face derived from them (a raised panel, the
168
+ softer rules, the fainter text, the darker accent of a pressed button), and whether Custom is
169
+ light or dark read off its page colour; **customise <theme>** copies the theme on screen into
170
+ the pickers so a custom scheme starts from a look. Saved on the server with the other settings,
171
+ so every screen of the monitor draws the same; the sign-in page draws in the theme the browser
172
+ last saw.
173
+ - The theme is applied through the CSSOM (the page's custom properties rewritten on `<html>`),
174
+ which the content-security policy permits where a style attribute is refused. The charts' axis
175
+ text, grid lines and tooltips, the order-book depth chart's two sides and the market charts'
176
+ grounds follow the theme — they were literals of the dark look, and a light theme would have
177
+ drawn grey-on-grey. The 3D boards stay space, and the Explorer's block and transaction pages
178
+ keep their own dark cards.
179
+
180
+ ### Mining
181
+
182
+ - **The Mining tab has the network row**: reward stats over the last 144 blocks, the difficulty
183
+ period (blocks remaining, the estimated and the previous change, the next halving), a week of
184
+ pools with luck and counts, a year of hashrate with the difficulty's steps, and the last twelve
185
+ adjustments as a table — every figure from this node alone (`server/collect/network.js`:
186
+ `getblockstats` for the reward window, block headers for the periods and a daily sample, the
187
+ week's coinbases read in the background at the lowest RPC priority).
188
+ - **mempool.space's layout**: two columns in its order (reward stats, difficulty adjustment;
189
+ pools, hashrate; recent blocks, adjustments) under the block flow, our own panels beneath, and
190
+ **View more »** on four cards opening that card full screen — the whole week of pools as a
191
+ table, the year's chart large, the whole attributed window of blocks, a year of adjustments.
192
+ - **The pool chart is a labelled pie**: every pool named beside its slice and joined to it by a
193
+ leader in the slice's colour, the small ones stacked, slices under half a percent gathered as
194
+ "Other" — and the "Other" threshold rises until every label fits the card, so no name runs off
195
+ the bottom at any window height. The pie and the year chart size themselves to the window.
196
+ - **Dollar figures** on the reward stats while market polling is on, from a new `/api/price` that
197
+ answers the cached spot price and never starts the exchange polling itself.
198
+ - The Mempool space viewer left this page — it is on Overview, Block space and Mempool — and with
199
+ it the full-pool poll the page used to ask for. The reward window is exactly 144 blocks.
200
+ - At a narrow window the block flow spans the width and is thinner, and the packages table scrolls
201
+ inside its card instead of bleeding into the neighbour; the three stacks become two below 1400px.
202
+ - **A streak of stale drops of the full-pool poll is one story**: one warning event when it
203
+ starts, a counter on Node & RPC while it lasts, one event when the poll answers again with the
204
+ count and the span. On a day the node answered slowly for thirteen hours, each drop had been its
205
+ own event, 188 of the feed's 200 rows.
206
+
207
+ ### Every tab on one screen
208
+
209
+ Measured at 1093×945 and 1600×1000 with a dead-height probe, page by page:
210
+
211
+ - **Globally**: tighter card padding and heading spacing, and a short card beside a tall one keeps
212
+ its own height instead of being stretched to match (Chain & Sync's drill-down card had a
213
+ thousand pixels of nothing under its input; Node & RPC's poll cadence and connection cards
214
+ hundreds).
215
+ - **Overview**: the block flow card a quarter shorter, and the Block space board capped to the
216
+ viewport (it set the whole right column's height), so the events are on the first screen.
217
+ - **Block space**: the card tighter and the board sized so the whole page, note included, fits the
218
+ viewport without a scroll.
219
+ - **Markets**: the board takes what the window leaves, and the exchange table and the order-book
220
+ depth sit side by side beneath it (under 1400px the table drops the pair, 24 h range and volume
221
+ columns; all still in the summary line and on the Kiosk) instead of the depth chart starting a
222
+ thousand pixels down.
223
+ - **Kiosk**: tighter heads and gaps, the price panel's padding trimmed, the Block flow strip a
224
+ quarter shorter, and the full-screen button in that strip's head instead of floating over its
225
+ cards.
226
+ - **Mempool**: keeps its two columns down to 900px (it stacked from 1100), and the room over goes
227
+ to its time-series charts, a fifth taller.
228
+ - **Explorer**: a smaller title, the block strip's padding and scrollbar band trimmed, tighter
229
+ table rows and the table reaching the window's bottom edge — three more blocks on the first
230
+ screen.
231
+ - **Peers**: a long onion or i2p address no longer makes the whole page scroll sideways (it clips
232
+ with an ellipsis and carries the full text as its title; a wide table scrolls inside its card),
233
+ rows and service badges tighter, the top strip's cards at their own heights.
234
+ - **Network**: Throughput beside a stack of Upload and Traffic accounting, the throughput chart
235
+ taller, and the sources table taking the rest of the window.
236
+ - **Events**: the feed reaches the window's bottom edge (it stopped a hundred pixels short).
237
+ - **Chain & Sync**: one-line notes under the block-size and tip charts (the longer sentences are
238
+ their tooltips), the transaction-rate figures as one wrapping line, values that keep to their
239
+ line, and a shorter uptime label — the first three rows sit level and the drill-down and indexes
240
+ reach the first screen.
241
+ - **Node & RPC**: two stacks (RPC etiquette with its figures in two columns, Data quality and Node
242
+ connection; Poll cadence, self-telemetry beside the log tail, Panel sources and the RPC
243
+ console) instead of grid rows that left holes beside the short cards.
244
+ - **Admin**: two stacks (Users over the audit log; Node actions over Change own password, whose
245
+ two fields share a row). **Sign-in** and **404**: the same tighter card.
246
+ - **Display settings**: the tabs in three labelled rows — Boards (Appearance, Block space, Sky,
247
+ Markets & Price), Effects, and the Diversions last; rows, note and groups tighter, the sheet a little wider so hints wrap
248
+ less, and on the two effects tabs the switches run two across with each hint as the row's
249
+ tooltip — the Space effects tab is under half the scrolling it was. The price line on Overview
250
+ switch sits directly under the polling switch it depends on.
251
+ - **About**: the sky runs to the bottom edge with the credit line at its foot, and the GitHub
252
+ link and the tip pill share a row.
253
+ - **The Diversions pages take the window**: the wells of Tetrust, Blockout and Blockanoid grow to
254
+ the window's height (they stopped at 600px), and the DOS screen is always 4:3 and as large as
255
+ the window allows — at a 1093px window the 320×200 picture had been drawn squashed into a
256
+ 761×720 box.
257
+
258
+ ### DOS Diversions
259
+
260
+ - **DOOM.** The shareware `DOOM.EXE` v1.9 and `DOOM1.WAD` from `games/doom_dos/`, unmodified,
261
+ running on a PC emulated in the browser with no dependencies: an i386 interpreter
262
+ (`public/js/x86.js`), the DOS/4GW machine around it — DOS, DPMI, BIOS, PIC, timer, keyboard and
263
+ a planar VGA (`dospc.js`) — and a Sound Blaster Pro 2 with an OPL3 for the effects and the music
264
+ (`soundcard.js`), in a worker. Demos, menus, savegames and ENDOOM all work; 35 frames a second.
265
+ Mouse capture, WASD keys by default (the switch rebinds the running game at once), smoothing,
266
+ full screen; pauses when you look away; savegames and DOOM's config stay in the browser.
267
+ - **Wolfenstein 3D.** The shareware `WOLF3D.EXE` v1.4 and its `.WL1` files from
268
+ `games/wolf3d_dos/`, unmodified, on the same emulated PC — which now also runs a 16-bit real-mode
269
+ DOS program: real mode in the CPU (segments times sixteen, a wrapping SP, 16-bit string
270
+ instructions and interrupt frames), a plain MZ loader with its relocations, DOS's memory
271
+ allocator, the interrupt vector table, and VGA write mode 1. Menus, demos, AdLib music,
272
+ digitised sound effects, savegames and high scores kept in the browser, at its full 70 frames a
273
+ second. It sits before DOOM and Quake in the Diversions menu.
274
+ - **Quake.** The shareware `QUAKE.EXE` v1.06 and `PAK0.PAK` from `games/quake_dos/`, unmodified,
275
+ on the same emulated PC — which now also plays the go32 stub and CWSDPMI for a DJGPP program:
276
+ segment bases and 16-bit code in the CPU, a COFF loader, a directory tree, DOS's system file
277
+ table, the BIOS tick count. Demos, menus, savegames, sound effects through the Sound Blaster,
278
+ and the order screen on quit. W A S D and mouse look on the first start, Quake's own bindings
279
+ after. Quake starts with a smaller view (`viewsize 80`); **−** and **=** change it and Quake
280
+ keeps the choice.
281
+ - **The emulated PC is fast.** Each instruction is decoded once and cached rather than decoded
282
+ every time it runs (writes into cached code clear what they overlap, because both games patch
283
+ their own drawing code), the arithmetic and the x87 forms Quake runs most have handlers of their
284
+ own, and memory addresses are formed inline: Quake from 77 to about 141 million instructions a
285
+ second, its `timedemo demo1` from 29.7 fps to over 50; in the browser Quake plays at 40–50
286
+ frames a second.
287
+ - **A way out of full screen**: an **exit full screen** button in the top corner while full
288
+ screen, and holding **Esc** for a second (a tap of Esc stays the game's menu, which is what the
289
+ keyboard lock is for); leaving unlocks the keyboard and releases the mouse.
290
+ - The game files are served at `/games/<game>/<path>` (`server/http/games.js`); the shareware
291
+ packages live under `games/` (DOOM's moved from `doom_dos/` at the repository root).
292
+
293
+ ### Markets and the effects
294
+
295
+ - **Black hole**, a Markets effect (modelled on NASA's visualisation, svs.gsfc.nasa.gov/14576): a
296
+ point of darkness opens and grows to a horizon over 26 s, the price line bends round it as
297
+ through a lens (the Einstein-radius push), the candles it reaches stretch and lean toward it and
298
+ shrink away into it, and an accretion disk seen nearly edge-on — a thin band white-yellow at its
299
+ inner edge through orange to a dark red rim, five hundred fibres spiralling inward sheared by
300
+ the Keplerian flow, its far side lensed into an arch over the shadow and a smaller one beneath,
301
+ a photon ring hugging the horizon, starlight bent into arcs. **It crosses the board**: it opens
302
+ a fifth of the way in on one side and runs a straight line through the chart to a fifth from the
303
+ other, from one height to another, so the line is seen bending round it as it passes; the
304
+ candles' orbits ride along at a quarter of the first cut's speed. It finishes its crossing before
305
+ it lets go, and lets go evenly, every candle gliding home and growing back smoothly. It hovered
306
+ over Block space for a day and its cubes never came home cleanly enough, so it is a Markets
307
+ effect only.
308
+ - **Supernova** (the solar flare, renamed and remade on NASA Goddard's animation,
309
+ svs.gsfc.nasa.gov/20413): the star is a glint — halo, hot core, diffraction spikes — growing and
310
+ blue-shifting as it goes; the opening explosion is a **white-hot flash**, held and washing far
311
+ out, that throws **a hundred and twenty-eight plumes of white gas** in every direction, each
312
+ flying and growing on its own out to nine star radii and drifting on until the effect is nearly
313
+ done (drawn on a half-resolution layer to keep the frame cheap); the debris is a volume of gas in
314
+ three layers — a blue cloud out in front, a white one behind it into which **violet grows patch
315
+ by patch from a tenth of the run**, and a deep violet heart — with a soft shock band throwing
316
+ and shaking the candles it crosses; and as the cloud dims a **pulsar** beats seven times a
317
+ second at the centre, two thin beams sweeping round, inside a nebula of the same gas that holds
318
+ its violet weight until four fifths of the run. 16 s (24 on Markets).
319
+ - **Fireworks are a display**, on both boards: every shell drawn — the rocket's arc, a white flash
320
+ and a shockwave ring at the burst, seven shell kinds by turn (peony, chrysanthemum, willow, ring,
321
+ crossette, strobe and more), sixty to ninety sparks each a curved trail, a lens flare at every
322
+ burst, glitter rain, and a nebula of smoke that drifts up and lingers; up to ten shells on
323
+ Markets over 14 s (five on Block space), each lighting the candles under it in its own colour.
324
+ **Off by default** on both boards, kept for special occasions.
325
+ - **X-ray**, on both boards: a front sweeps the board and everything behind it goes x-ray — bodies
326
+ dimmed to glass, every edge lit, a raster of thin lines across each face — then develops back.
327
+ - **Breathe** and **Light saber**, on the price line: three slow swells from the plain wire to
328
+ white heat and back; and the line igniting from its left end, blue, green, red or purple by the
329
+ run, humming with a breathing glow, spitting sparks, retracting to the hilt.
330
+ - **The energy pulse is heat, not blue, and its head is a crackling ball of plasma**; **the scan
331
+ line is a light curtain** standing on the board floor to the chart's top; **ball lightning is a
332
+ light source**, its arcs discharges built by midpoint displacement rather than zigzags, a quarter
333
+ of its size and a third of its speed on Markets; the scanner's cone is dimmer on both boards.
334
+ - **The Markets board keeps its price fit across refreshes**: the last fit is kept while the data
335
+ still sits inside it and fills at least two-thirds of it, so a refresh no longer re-scales the
336
+ whole chart unanimated. Whatever a head pulls is painted last, nearest the head on top; a held
337
+ pool refresh waits a moment and a half after an idle effect ends, with the board whole and still.
338
+ - **Layered soft fills add up correctly** (`softStops`): each nested disc carries only its
339
+ increment, so a stop's cumulative opacity is exactly the stop's; measured on the Kiosk, the
340
+ supernova's peak opacity fell from about 0.9 to about 0.45 and the chart shows through it.
341
+ - **The no-repeat window holds from the first pick** on both boards: every effect plays once
342
+ before any repeats (a negative slice index had let one come round at the tenth pick). The
343
+ searchlight and the tractor beam, which fly the same saucer, share the window.
344
+ - **A Kiosk test suite** (`test/kiosk.test.js`): every Markets and Block space effect plays through
345
+ the frame loop on a Kiosk-sized panel; the supernova, the black hole and the fireworks are
346
+ bounded by the board's width so no panel is covered.
347
+
348
+ ### Fixed
349
+
350
+ - **The donation QR shows on Safari.** It was an inline SVG of 800 module rects, and Safari on
351
+ the first Mac install painted its white ground and none of the rects. It is a PNG now, painted
352
+ the same everywhere.
353
+ - **A dead index worker fails the build instead of hanging it.** A worker killed outright (out of
354
+ memory, with four beside the node) left its job unanswered and the build sat at "scan 5,720 of
355
+ 5,721, about 1 s left" for an hour and a half on the first Mac install. The run now rejects the
356
+ moment a worker exits or throws, naming the job and the fix (fewer `addressIndexWorkers`); the
357
+ progress flag says *no progress for N min* when nothing has moved.
358
+ - The 0.0.9 release notes' test count corrected (862, not 864).
359
+
360
+ ### Documentation
361
+
362
+ - INSTALL, GETTING-STARTED, SECURITY, CONFIGURATION, API, TROUBLESHOOTING and the README describe
363
+ the hardened defaults, HTTPS and the polling switch; INSTALL §11 has the notes for updating a
364
+ 0.0.9 install; the USER-GUIDE has the Appearance section, the Mining row and the packed layouts;
365
+ TROUBLESHOOTING explains a streak of stale pool-poll drops. Screenshots re-shot at 0.1.0, with a
366
+ Mining shot added. The announcement for the bitcointalk thread is in `docs/announcement/0.1.0/`.
10
367
 
11
368
  ## [0.0.9] — 2026-09-14
12
369
 
@@ -676,4 +1033,5 @@ are an original implementation (`public/js/blockpack.js`, `public/js/feepalette.
676
1033
  Internal first version: multi-user monitor with charts, sync viewer, mempool view, peers and
677
1034
  event feed.
678
1035
 
1036
+ [0.0.10]: https://github.com/BobClawblaw/blockyard/releases/tag/v0.0.10
679
1037
  [0.0.9]: https://github.com/BobClawblaw/blockyard/releases/tag/v0.0.9
package/README.md CHANGED
@@ -5,13 +5,13 @@ A live, multi-user web monitor and block explorer for
5
5
 
6
6
  Point it at your node and open a browser: live charts, a 3D block-space viewer,
7
7
  a block / transaction / address explorer, exchange prices with order-book depth, and a
8
- kiosk view for a wall screen. No dependencies to install, no CDN, no telemetry, and
9
- read-only toward your node by default.
8
+ kiosk view for a wall screen. No dependencies to install, no CDN, no telemetry, no outbound
9
+ connection but to your node out of the box, and read-only toward your node by default.
10
10
 
11
11
  **This is 100% machine-generated code, directed by a human operator.** Every line of the
12
12
  server, the browser app, the 3D engine, the tests and these documents was written by an AI
13
13
  under a human's direction, and all auditing has been performed by AI and is published in this
14
- repository ([docs/SECURITY-AUDIT.md](docs/SECURITY-AUDIT.md), [docs/SECURITY-AUDIT-2026-09-14.md](docs/SECURITY-AUDIT-2026-09-14.md), [docs/DEFECTS.md](docs/DEFECTS.md),
14
+ repository ([docs/SECURITY-AUDIT.md](docs/SECURITY-AUDIT.md), [docs/SECURITY-AUDIT-2026-09-14.md](docs/SECURITY-AUDIT-2026-09-14.md), [docs/SECURITY-AUDIT-2026-09-16.md](docs/SECURITY-AUDIT-2026-09-16.md), [docs/DEFECTS.md](docs/DEFECTS.md),
15
15
  [docs/MEASUREMENTS.md](docs/MEASUREMENTS.md)). It is **experimental pre-release software: expect
16
16
  bugs.**
17
17
 
@@ -21,22 +21,23 @@ bugs.**
21
21
 
22
22
  | | |
23
23
  |---|---|
24
- | **Block space, in 3D.** The next block's worth of the mempool as a board of glowing tiles: area is vbytes, colour is feerate. Refreshes are choreographed — blocks lift, travel in collision-free lanes and land under gravity — and the board comes alive at rest with **30 idle effects**, from ripples and light cycles to a lightning ball, ball lightning, a UFO's tractor beam, Missile Command, fireworks, code rain and a demoscene plasma. Two viewer modes: **Simple** (the richest few hundred transactions as cubes) and **Detailed** (every transaction in the block). | ![Block space, Detailed mode](docs/images/block-space-mode2.jpg) |
25
- | **A board you can tune.** Neon-tube blocks, a metallic sheen or a chrome finish that mirrors a horizon, a movable lamp, a touch of perspective, a spiral galaxy behind the board, and a switch for every one of the 30 effects — in a tabbed settings panel. Stored on the server (`config/blockyard.json`), so every screen sees the same board; they change how things are *drawn*, never what is measured. | ![Neon blocks and the metallic sheen](docs/images/block-space-neon.jpg) |
24
+ | **Block space, in 3D.** The next block's worth of the mempool as a board of glowing tiles: area is vbytes, colour is feerate. Refreshes are choreographed — blocks lift, travel in collision-free lanes and land under gravity — and the board comes alive at rest with **34 idle effects**, from ripples and light cycles to a lightning ball, ball lightning, a UFO's tractor beam, Missile Command, fireworks, code rain and a demoscene plasma. Two viewer modes: **Simple** (the richest few hundred transactions as cubes) and **Detailed** (every transaction in the block). | ![Block space, Detailed mode](docs/images/block-space-mode2.jpg) |
25
+ | **A board you can tune.** Neon-tube blocks, a metallic sheen or a chrome finish that mirrors a horizon, a movable lamp, a touch of perspective, a spiral galaxy behind the board, and a switch for every one of the 34 effects — in a tabbed settings panel. Stored on the server (`config/blockyard.json`), so every screen sees the same board; they change how things are *drawn*, never what is measured. | ![Neon blocks and the metallic sheen](docs/images/block-space-neon.jpg) |
26
26
  | **An explorer that looks the part.** Search a height, block hash, txid or address. Transaction pages with fee, fee rate and dollar value, feature badges, a flow diagram from inputs to outputs, and links to where every coin came from and went. **Address pages with full history and balance** — Bitcoin Core has no address index, so BlockYard builds its own from the node's block files (**a few hours** on first start, 124 GB) and keeps it current as blocks arrive. | ![Explorer transaction](docs/images/explorer-tx.jpg) |
27
- | **Markets.** Five exchanges' public prices: a 3D candle chart with a neon price line, a precise flat candlestick chart, an exchange table, and a bitcoinity-style order-book depth chart with change bars. Fetched by the server only while someone is looking. | ![Markets](docs/images/markets.jpg) |
27
+ | **Markets.** Five exchanges' public prices: a 3D candle chart with a neon price line, a precise flat candlestick chart, an exchange table, and a bitcoinity-style order-book depth chart with change bars. Off until you tick **Enable market polling** — it is the one thing that talks to anyone but your node — and, once on, fetched by the server only while someone is looking. | ![Markets](docs/images/markets.jpg) |
28
28
  | **Kiosk.** The 3D markets board, a price panel and the block-space board side by side, full screen with one click. | ![Kiosk](docs/images/kiosk.jpg) |
29
- | **Tetrust, Blockout and Blockanoid.** Three playable games built on the same 3D engine — trust, but verify. Tetrust is Tetris: the well is the block-space board and the music is synthesised in the browser. Blockout is Breakout, where the wall is made of block-space stones and the bat follows your mouse. Blockanoid is Arkanoid: a different wall every level, silver bricks that take more than one hit, gold that takes none, and capsules that fall out of what you break — laser, wide, catch, slow, three balls, a life. All three pause when you look away and keep high scores per browser. | ![Tetrust](docs/images/tetrust.jpg) |
29
+ | **Tetrust, Blockout and Blockanoid.** Three playable games built on the same 3D engine — trust, but verify. Tetrust is Tetris: the well is the block-space board and the music is synthesised in the browser. Blockout is Breakout, where the wall is made of block-space stones and the bat follows your mouse. Blockanoid is Arkanoid: a different wall every level, silver bricks that take more than one hit, gold that takes none, and capsules that fall out of what you break — laser, wide, catch, slow, three balls, a life. All three pause when you look away and keep high scores per browser. **Scorched Yard** is Scorched Earth on the same engine: a landscape of dirt cubes that craters and falls, the original's thirty-three weapons and eleven accessories at the manual's prices, its eight computer personalities, cash and a shop between rounds, and the wind you can see. And **Wolfenstein 3D**, **DOOM** and **Quake**: the real shareware `WOLF3D.EXE` (1992), `DOOM.EXE` (1993) and `QUAKE.EXE` (1996), unmodified, on a PC emulated in the browser — processor in real and protected mode, FPU, DOS and both extenders, VGA and Sound Blaster written from scratch, still with no dependencies. So if anyone asks *"well, can it run DOOM?"*, the answer is an emphatic **"Naturally. What sort of AI slop generator do you take me for?"** | ![Tetrust](docs/images/tetrust.jpg) |
30
30
 
31
31
  Also on board: a sync viewer with an honest ETA, Block flow (projected blocks, the block
32
32
  being built, recent blocks), mempool and fee charts, a peer table, bandwidth, the node's
33
33
  event stream, a provenance table for every figure, a read-only RPC console behind a
34
34
  default-deny allowlist, and a tabbed **Display settings** panel (the gear) that tunes every
35
- board without a reload.
35
+ board without a reload and picks the layout's colours — light or dark, the shipped look, Mono,
36
+ Nous, GitHub, Catppuccin, or nine colours of your own.
36
37
 
37
38
  ## Quick start
38
39
 
39
- You need **Node.js 22 or newer** and a running **Bitcoin Core 25.0 or later** with `server=1`
40
+ You need **Node.js 22.2 or newer** and a running **Bitcoin Core 25.0 or later** with `server=1`
40
41
  and `txindex=1`, **on the same machine** — BlockYard reads the node's block files to build the
41
42
  explorer's address index, and a node on another machine is not supported. Without `txindex` the
42
43
  explorer cannot look a confirmed transaction up by id; everything else works without it (see
@@ -46,9 +47,10 @@ top of the node's own ~875 GB of block files.
46
47
  ```bash
47
48
  git clone https://github.com/BobClawblaw/blockyard.git
48
49
  cd blockyard
49
- npm test # optional: 862 unit tests, all built in
50
+ npm test # optional: 1068 unit tests, all built in
50
51
  npm run setup # reads the node's bitcoin.conf, checks the node, writes config/local.json
51
- npm start # builds the address index in the background (a few hours); open http://127.0.0.1:21000
52
+ npm start # builds the address index in the background (a few hours); open https://127.0.0.1:21000
53
+ # and sign in as admin with the password the first start prints once
52
54
  ```
53
55
 
54
56
  `npm run setup` asks for the node's data directory, reads its `bitcoin.conf` for the rest,
@@ -107,15 +109,23 @@ design rests on) and [docs/DEFECTS.md](docs/DEFECTS.md) (known limits).
107
109
 
108
110
  ## Security at a glance
109
111
 
110
- - **Open, read-only access by default** — like a block explorer, anyone who can reach the
111
- port can look. Accounts, roles, sessions, CSRF protection and a per-user audit trail turn
112
- on with `BLOCKYARD_AUTH=1`.
113
- - **Where it listens is your decision** bind to `127.0.0.1`, a LAN address, a VPN
114
- address, or several. Built-in HTTPS with `BLOCKYARD_TLS_CERT` / `BLOCKYARD_TLS_KEY`.
115
- - **Outbound connections are limited and on demand**: exchange APIs only while someone has
116
- the Markets, Kiosk or Overview tab open Overview's price line is on by default, so the landing
117
- page reaches out unless you switch it off plus a cached spot price for the explorer's dollar
118
- figures. `BLOCKYARD_MARKETS=0` turns all of it off.
112
+ - **Hardened out of the box** — it listens on `127.0.0.1` only and requires sign-in: the
113
+ first start creates an `admin` account and prints its password once. Accounts, roles,
114
+ sessions, CSRF protection and a per-user audit trail are on. Reach it from another machine
115
+ over an SSH tunnel, or bind a LAN address (`BLOCKYARD_BIND`) when you decide the LAN may
116
+ see it; open, read-only access with no account is a choice (`BLOCKYARD_AUTH=0`), announced
117
+ at boot.
118
+ - **HTTPS by default**the first start makes the monitor its own certificate (no
119
+ dependencies: the X.509 is written by hand), and every listener serves it; a certificate of
120
+ your own with `BLOCKYARD_TLS_CERT` / `BLOCKYARD_TLS_KEY`, or `BLOCKYARD_TLS=0` behind a
121
+ proxy that terminates TLS.
122
+ - **Where it listens is your decision** — `127.0.0.1`, a LAN address, a VPN address, or
123
+ several.
124
+ - **No outbound connections out of the box**: a fresh install talks to nothing but your node.
125
+ The one exception is opt-in — a checkbox, **Display settings → Markets & Price → Enable
126
+ market polling**, turns on the exchange feed for the Markets and Kiosk tabs, Overview's price
127
+ line and the explorer's dollar figures, and even then the server asks the exchanges only while
128
+ someone is looking. `BLOCKYARD_MARKETS=0` removes the feed so that no checkbox can turn it on.
119
129
 
120
130
  Details in [docs/SECURITY.md](docs/SECURITY.md). To report a vulnerability, see
121
131
  [SECURITY.md](SECURITY.md).
@@ -126,7 +136,7 @@ Details in [docs/SECURITY.md](docs/SECURITY.md). To report a vulnerability, see
126
136
  npm run dev # fake node doing a simulated sync, port 18088
127
137
  npm run setup # interactive install: read bitcoin.conf, check the node, write config/local.json
128
138
  npm run check # the same checks (every call timed) against every configured node; exits 1 on a FAIL
129
- npm test # 862 unit tests (node:test, no dependencies)
139
+ npm test # 1068 unit tests (node:test, no dependencies)
130
140
  npm run smoke # boots the real server and checks the HTTP contract
131
141
  npm run counts:fix # keep the documented test count in step with the suite
132
142
  ```
@@ -136,20 +146,31 @@ RPC etiquette) and [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for how it fits
136
146
 
137
147
  ## Status
138
148
 
139
- Version **0.0.9** — the initial release, and pre-release software: the word is meant literally. The test suite is
140
- comprehensive (862 tests, plus a live smoke run), the monitoring side is solid, and the
149
+ Version **0.1.1** (2026-09-17) a hardening release after 0.1.0: the third security audit with
150
+ every finding fixed, a resumable address index build, and Scorched Yard. Pre-release software: the
151
+ word is meant literally. Releases are published on [npm](https://www.npmjs.com/package/blockyard)
152
+ as `blockyard`, as [GitHub releases](https://github.com/BobClawblaw/blockyard/releases),
153
+ and announced on
154
+ [bitcointalk](https://bitcointalk.org/index.php?topic=5594141.msg67144312) — questions, bug
155
+ reports and reviews are welcome there and in [issues](https://github.com/BobClawblaw/blockyard/issues).
156
+ It ships **hardened**: bound to this machine, sign-in on, HTTPS with a certificate it makes itself,
157
+ and **zero telemetry** — no outbound connection to anyone but your node until you tick the market
158
+ polling switch yourself.
159
+ The test suite is
160
+ comprehensive (1068 tests, plus a live smoke run), the monitoring side is solid, and the
141
161
  explorer's biggest gap is closed: **address history and balances**, which Bitcoin Core cannot
142
162
  answer at any setting, now come from an **address index BlockYard builds itself** from the
143
163
  node's block and undo files and keeps current as blocks arrive. It is checked against the node
144
- (every balance equal to `scantxoutset`, to the satoshi) and costs ~30 minutes on 16 workers and
145
- 124 GB of disk, built in the background the first time BlockYard starts and paced so the node's
164
+ (every balance equal to `scantxoutset`, to the satoshi) and costs a few hours on the default four
165
+ workers (~30 minutes on 16) and 124 GB of disk, built in the background the first time BlockYard starts and paced so the node's
146
166
  RPC stays responsive; without one, the address page says *not indexed* rather than showing a
147
167
  zero. An address's unspent outputs are listed too (for a history of up to 100 transactions).
148
168
  What it does not yet have: an address's mempool transactions.
149
169
 
150
170
  Everything here was written by an AI directed by a human, and audited by AI:
151
- [docs/SECURITY-AUDIT.md](docs/SECURITY-AUDIT.md) and
152
- [docs/SECURITY-AUDIT-2026-09-14.md](docs/SECURITY-AUDIT-2026-09-14.md) are the audits, findings
171
+ [docs/SECURITY-AUDIT.md](docs/SECURITY-AUDIT.md),
172
+ [docs/SECURITY-AUDIT-2026-09-14.md](docs/SECURITY-AUDIT-2026-09-14.md) and
173
+ [docs/SECURITY-AUDIT-2026-09-16.md](docs/SECURITY-AUDIT-2026-09-16.md) are the audits, findings
153
174
  and remediation included. The test suite runs in CI on Ubuntu, macOS and Windows (Node 22 and 24);
154
175
  a real install has been done on macOS (Core 29.1) and Linux, and Windows has only the test suite.
155
176
  [docs/DEFECTS.md](docs/DEFECTS.md) lists five open items, honestly stated, with the
package/SECURITY.md CHANGED
@@ -4,8 +4,8 @@
4
4
 
5
5
  | version | supported |
6
6
  |---|---|
7
- | 0.0.9 | yes |
8
- | anything else | no (never released) |
7
+ | 0.1.x | yes |
8
+ | 0.0.9 and earlier | no: upgrade. 0.0.9 shipped bound to every interface with no sign-in, and later audits fixed issues it still has |
9
9
 
10
10
  ## Reporting a vulnerability
11
11
 
package/bin/blockyard.js CHANGED
@@ -27,11 +27,12 @@ const COMMANDS = {
27
27
  start: 'server/main.js',
28
28
  'index-build': 'scripts/index-build.js',
29
29
  users: 'scripts/manage-users.js',
30
+ tls: 'scripts/tls.js',
30
31
  };
31
32
  const cmd = process.argv[2];
32
33
  if (!COMMANDS[cmd]) {
33
34
  const version = JSON.parse((await import('node:fs')).readFileSync(path.join(root, 'package.json'), 'utf8')).version;
34
- process.stdout.write(`BlockYard ${version}\n\n blockyard setup ask where the node is, check it, write the config, build the index\n blockyard start run the monitor\n blockyard check the same checks as setup, any time\n blockyard index-build build the address index by hand (--out <dir> [--workers N])\n blockyard users manage accounts\n\nconfig ${process.env.BLOCKYARD_CONFIG}\ndata ${process.env.BLOCKYARD_DATA}\n`);
35
+ process.stdout.write(`BlockYard ${version}\n\n blockyard setup ask where the node is, check it, write the config, build the index\n blockyard start run the monitor\n blockyard check the same checks as setup, any time\n blockyard index-build build the address index by hand (--out <dir> [--workers N])\n blockyard users manage accounts\n blockyard tls remake this monitor's own HTTPS certificate (--san to add names)\n\nconfig ${process.env.BLOCKYARD_CONFIG}\ndata ${process.env.BLOCKYARD_DATA}\n`);
35
36
  process.exit(cmd ? 2 : 0);
36
37
  }
37
38
  // the scripts decide "am I being run directly?" by comparing argv[1] to their own path, so hand