blockyard 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/CHANGELOG.md +929 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +4 -0
  4. package/README.md +191 -4
  5. package/SECURITY.md +38 -0
  6. package/bin/blockyard.js +41 -0
  7. package/config/pool-map.json +2620 -0
  8. package/docs/API.md +1577 -0
  9. package/docs/ARCHITECTURE.md +1394 -0
  10. package/docs/AUTO-UPDATE.md +269 -0
  11. package/docs/CONFIGURATION.md +847 -0
  12. package/docs/DEFECTS.md +813 -0
  13. package/docs/EFFECTS-AGENTS.md +448 -0
  14. package/docs/GETTING-STARTED.md +205 -0
  15. package/docs/INSTALL.md +547 -0
  16. package/docs/MEASUREMENTS.md +1401 -0
  17. package/docs/RULES.md +681 -0
  18. package/docs/SECURITY-AUDIT-2026-09-14.md +177 -0
  19. package/docs/SECURITY-AUDIT.md +258 -0
  20. package/docs/SECURITY.md +212 -0
  21. package/docs/TROUBLESHOOTING.md +332 -0
  22. package/docs/USER-GUIDE.md +1262 -0
  23. package/package.json +53 -5
  24. package/public/404.html +9 -0
  25. package/public/css/app.css +2009 -0
  26. package/public/donate-qr.png +0 -0
  27. package/public/index.html +1085 -0
  28. package/public/js/about.js +112 -0
  29. package/public/js/agents.js +1141 -0
  30. package/public/js/app.js +1386 -0
  31. package/public/js/arkanoid.js +806 -0
  32. package/public/js/blockanoid.js +347 -0
  33. package/public/js/blockout.js +347 -0
  34. package/public/js/blockpack.js +428 -0
  35. package/public/js/blockscene3d.js +2830 -0
  36. package/public/js/breakout.js +224 -0
  37. package/public/js/charts.js +635 -0
  38. package/public/js/depthchart.js +315 -0
  39. package/public/js/details3d.js +4342 -0
  40. package/public/js/doom.js +31 -0
  41. package/public/js/dosaudio.js +48 -0
  42. package/public/js/dosgame.js +389 -0
  43. package/public/js/dosio.js +186 -0
  44. package/public/js/dospc.js +1353 -0
  45. package/public/js/dosworker.js +196 -0
  46. package/public/js/explorer.js +405 -0
  47. package/public/js/feepalette.js +149 -0
  48. package/public/js/fmt.js +162 -0
  49. package/public/js/goggles.js +886 -0
  50. package/public/js/kiosk.js +41 -0
  51. package/public/js/login.js +88 -0
  52. package/public/js/markets.js +395 -0
  53. package/public/js/mining.js +1416 -0
  54. package/public/js/panels.js +970 -0
  55. package/public/js/pricechart.js +189 -0
  56. package/public/js/quake.js +20 -0
  57. package/public/js/settings.js +1096 -0
  58. package/public/js/soundcard.js +459 -0
  59. package/public/js/tetris.js +226 -0
  60. package/public/js/tetrust.js +356 -0
  61. package/public/js/tetsound.js +175 -0
  62. package/public/js/theme.js +235 -0
  63. package/public/js/wolf3d.js +22 -0
  64. package/public/js/x86.js +1978 -0
  65. package/public/login.html +33 -0
  66. package/scripts/blockfile-measure.js +156 -0
  67. package/scripts/browser-check.mjs +286 -0
  68. package/scripts/check.js +173 -0
  69. package/scripts/decode-check.js +81 -0
  70. package/scripts/doc-counts.js +109 -0
  71. package/scripts/donate-qr.py +23 -0
  72. package/scripts/dos-bench.js +56 -0
  73. package/scripts/fake-node.js +534 -0
  74. package/scripts/index-bench.js +216 -0
  75. package/scripts/index-benchmark.js +117 -0
  76. package/scripts/index-build.js +40 -0
  77. package/scripts/live-render-check.mjs +89 -0
  78. package/scripts/manage-users.js +132 -0
  79. package/scripts/motion-check.mjs +138 -0
  80. package/scripts/pool-map.js +157 -0
  81. package/scripts/setup.js +432 -0
  82. package/scripts/shots.mjs +278 -0
  83. package/scripts/smoke.sh +327 -0
  84. package/scripts/tls.js +31 -0
  85. package/scripts/ui.js +174 -0
  86. package/server/auth/sessions.js +221 -0
  87. package/server/auth/users.js +243 -0
  88. package/server/chain/blockfile.js +234 -0
  89. package/server/chain/index/build.js +210 -0
  90. package/server/chain/index/heights.js +36 -0
  91. package/server/chain/index/live.js +276 -0
  92. package/server/chain/index/rows.js +145 -0
  93. package/server/chain/index/store.js +154 -0
  94. package/server/chain/index/worker.js +109 -0
  95. package/server/chain/tx.js +310 -0
  96. package/server/collect/gbt.js +229 -0
  97. package/server/collect/logparse.js +765 -0
  98. package/server/collect/logtail.js +189 -0
  99. package/server/collect/markets.js +333 -0
  100. package/server/collect/mining.js +333 -0
  101. package/server/collect/monitor.js +2545 -0
  102. package/server/collect/network.js +295 -0
  103. package/server/collect/nextblock.js +275 -0
  104. package/server/collect/sync.js +386 -0
  105. package/server/config.js +644 -0
  106. package/server/http/api.js +1319 -0
  107. package/server/http/explorer.js +418 -0
  108. package/server/http/games.js +77 -0
  109. package/server/http/server.js +420 -0
  110. package/server/http/sse.js +176 -0
  111. package/server/http/static.js +212 -0
  112. package/server/main.js +673 -0
  113. package/server/netinfo.js +253 -0
  114. package/server/rpc/allowlist.js +130 -0
  115. package/server/rpc/client.js +414 -0
  116. package/server/store/audit.js +148 -0
  117. package/server/store/history.js +220 -0
  118. package/server/store/ledger.js +290 -0
  119. package/server/store/ring.js +173 -0
  120. package/server/tls/selfsigned.js +160 -0
  121. package/server/util/fmt.js +29 -0
  122. package/systemd/blockyard.service +102 -0
@@ -0,0 +1,448 @@
1
+ # Agent effects: a catalogue
2
+
3
+ (operator, 2026-09-13: *"no more per-tile stuff. Think more tron lightcycles stuff"*, then
4
+ *"Give me at least 25-50 new video game inspired effects. Examine all video games, and explain
5
+ your choices"*, then *"we can even add flying ships or other pixel-graphic inspired art"*.)
6
+
7
+ This is a design document. Thirty-two of the fifty were built on 2026-09-13 (see **Built so far**
8
+ below, kept as the record of that day) and most were then removed by the operator after seeing
9
+ them on the board; the seven agents present today are the table in the next section. The
10
+ numbering here is what the commits refer to.
11
+
12
+ ---
13
+
14
+ ## What was removed, and what stayed
15
+
16
+ **2026-09-13, after seeing them on the board, the operator removed twenty-three effects**:
17
+ Mystery ship, De-res, Pipeline, Bombing run, Traffic, Ramp run, Options, Lemmings, Katamari,
18
+ Scan visor, Lock-on, Claim, Tetromino, Asteroids, Squadron, Maze chase, Blast, Invaders, Hopper,
19
+ Snake, Identity disc, Recognizer, and the Glitch field effect.
20
+
21
+ That is twenty-two of the thirty-two agents and one field. A second pass the same day removed
22
+ **Laser, Lanes (`tempest`) and Sweep (`minesweeper`)**, and a third removed **Marble**.
23
+ **Twenty-nine effects remained: seven agents and twenty-two fields** — thirty since 2026-09-14, with the Markets pipe bulge, a line effect like the pulse.
24
+
25
+ The agents that stayed:
26
+
27
+ | kind | what it is |
28
+ |---|---|
29
+ | `lightcycle` | two riders laying light walls until one crashes |
30
+ | `ball` | the lightning ball tracing the grid |
31
+ | `centipede` | a body that weaves down and splits in two |
32
+ | `tractor` | a saucer that draws the tallest transaction up and puts it back |
33
+ | `missile` | arcs raining down against interceptors rising to meet them |
34
+ | `boulderdash` | the board gives way from a point, cubes collapsing outward |
35
+ | `stormball` | ball lightning drifting across the whole view, off-screen to off-screen; its arcs electrify the blocks they strike (replaced `portal`, 2026-09-14) |
36
+
37
+ Two consequences worth knowing. **No surviving agent HIDES a cube** -- boulder dash shortens rather
38
+ than absorbs -- so the `hide` path is now covered only by the synthetic end-to-end test in
39
+ `agents.test.js`, not by any agent in the wild. And **no surviving agent reads the skyline as
40
+ terrain**: `bomberman` and then `marble` were the two that did, and both went, taking their
41
+ data-aware tests with them. The flat-board guard below still plays every registered agent on a
42
+ uniform board, so the lesson outlives the effects that taught it -- it is the guard, not the
43
+ effects, that keeps the next skyline-reader honest.
44
+
45
+ Stored settings need no migration: `normalise` rebuilds the effects map from `DEFAULTS` and drops
46
+ keys it does not know, so a `config/blockyard.json` naming a removed effect simply loses it.
47
+
48
+ ## What separates an agent effect from what we already have
49
+
50
+ The board has thirty idle effects (when this was written, twenty-six). Twenty-three of them are
51
+ **fields**: pure functions of a tile's position and the effect's clock,
52
+ `fxAt(tile, fx) -> {glow, outline, lift, color}`. A field effect has no location of its own — it
53
+ is a pattern evaluated everywhere at once. Plasma, aurora, checker, ripple, quake: every one is a
54
+ formula over `(cx, cy, u)`. (Two of them, `pulse` and `bulge`, play on the Markets price line
55
+ rather than on cubes.)
56
+
57
+ Seven are **agents** — the table above; when this was written there were two, `lightcycle` and
58
+ `ball`, plus a `packetPaths` primitive no effect uses. An agent has a *position*, a *route it has
59
+ already travelled*, and a *future*. It is drawn as its own geometry over the cubes (`drawCycles`,
60
+ `drawBall`, or the kind's own `draw` in `agents.js`) and it lights the board through `fx.heads` —
61
+ the cubes it is near flash in its colour. That is the difference the operator is pointing at: a
62
+ field is wallpaper, an agent is **something happening**, and you can watch it and wonder what it
63
+ will do.
64
+
65
+ The existing machinery for agents:
66
+
67
+ | primitive | what it gives | file |
68
+ |---|---|---|
69
+ | `cyclePath(seed, W, H, from)` | a right-angled route edge to edge, jinking | `blockscene3d.js` |
70
+ | `ballPath(seed, W, H, from, E)` | the same, entered from `E` units off-board | `blockscene3d.js` |
71
+ | `packetPaths(seed, W, H, n)` | `n` short wandering walks, each with its own start | `blockscene3d.js` |
72
+ | `cycleCrashes(paths)` | where two riders collide, worked out up front | `blockscene3d.js` |
73
+ | `cellTops(tiles, W, H)` | the height of the cube under every grid cell | `blockscene3d.js` |
74
+ | `pathHeights(pts, tops, W, H)` | what a route rides over, stepping at corners | `blockscene3d.js` |
75
+ | `chargeTrail(ctx, segs, lw, now)` | the shared electrical wake: puffs, tube, crackle, motes | `details3d.js` |
76
+ | `fx.heads` | `[{x, y, color, alpha, r, lift}]` — how an agent lights cubes. `r` is its reach in GRID UNITS (default 0.8: a gantry lights a swath, a thrown disc lights a point), `lift` throws the cubes it passes | `fxAt` |
77
+ | `AGENTS` registry | `{ build, frame, draw }` per kind; details3d.js keeps only three seams | `agents.js` |
78
+ | `view.unit` | pixels per grid unit — **size every sprite against this, never against `lw`** | `project` |
79
+
80
+ **Two hard rules any new effect must satisfy**, both enforced by tests:
81
+
82
+ 1. `effects.test.js` plays every `FX_KINDS` entry through `fxAt` and demands it lights something
83
+ (`worst > 0.3`). Only `pulse` and `bulge` are exempt, because they draw on the price line. **So every new
84
+ agent must publish `fx.heads`** and join the heads branch — which is also what makes it feel
85
+ connected to the board rather than painted over it.
86
+ 2. `viewer-canvas-rules.test.js`: no `ctx.clip()`, no `globalAlpha`, no composite modes, no
87
+ `shadowBlur`. Every glow is layered plain `rgba()` fills. This is not a style preference — the
88
+ software rasteriser drops clipped fills and honours `globalAlpha` inconsistently.
89
+
90
+ And one rule that is not enforced but matters: **the board is data**. Each cube is a transaction,
91
+ sized by vbytes and coloured by feerate. An effect that reads the data it crosses is worth more
92
+ than one that ignores it — `cascade` already does this (it runs richest-to-cheapest, so the
93
+ effect *is* a reading of the fee structure). The best entries below do the same.
94
+
95
+ ---
96
+
97
+ ## The catalogue
98
+
99
+ Fifty, grouped by what kind of agent they are. Each says what it does, why it suits *this* board,
100
+ and roughly what it costs. "Data-aware" marks the ones that read the transactions they cross.
101
+
102
+ ### I. Riders — things that travel a route and leave a wake
103
+
104
+ The `lightcycle` family. Cheap: one polyline, drawn stretch by stretch.
105
+
106
+ **1. Recognizer** (Tron) — the marching gantry: two legs and a crossbar, striding the board on a
107
+ straight line, lighting a wide swath *under* it rather than a point. Why: our cubes are a city
108
+ from above, and the Recognizer is the canonical "something is patrolling your city" silhouette.
109
+ It also solves a problem the light cycles have — they are thin, and on a dense 96-unit board a
110
+ 1-unit wall is faint. A gantry is wide. Cost: low.
111
+
112
+ **2. Identity disc** (Tron) — thrown from one edge, ricochets off the board's walls three or four
113
+ times, returns. Each bounce flashes the cubes at the impact point. Why: ricochet geometry gives
114
+ *anticipation* — you can see where it will land before it does, which no field effect can offer.
115
+ Cost: low. Route is `ballPath` with reflection instead of jinking.
116
+
117
+ **3. Snake** (Nibbler/Snake) — a segmented body that grows as it eats: it heads for the *tallest*
118
+ cube on the board, consumes it (a flash), grows two segments, picks the next. **Data-aware** — it
119
+ is literally eating the biggest transactions first, and the body length tells you how many it has
120
+ taken. Cost: low, and the pathfinding is greedy-nearest, not A*.
121
+
122
+ **4. Centipede** (Centipede) — a column of segments weaving down the board, dropping a row each
123
+ time it hits an edge. Splits into two independent centipedes if something interrupts its middle.
124
+ Why: the split is the memorable part, and our board is a grid of obstacles, which is exactly the
125
+ mushroom field Centipede needs.
126
+
127
+ **5. Pac-Man** — a mouth running the grid *lines* (not the cells), eating pellets at each
128
+ intersection; four ghosts on their own routes, each with a different pursuit rule (chase, ambush,
129
+ random, scatter). Why: four agents with *different personalities* on one board is the most legible
130
+ "something is happening" our engine could show, and the routes are already right-angled.
131
+
132
+ **6. Sonic loop** — a rider that accelerates downhill and launches off the top of a tall cube,
133
+ arcing through the air and landing further on. Why: our cubes have real heights (`cellTops`), and
134
+ nothing currently uses height as *terrain*. This makes the skyline matter.
135
+
136
+ **7. Excitebike ramp run** — same idea, low and fast, with a landing shockwave.
137
+
138
+ **8. Road Rash / OutRun sweeper** — a horizon-to-foreground racer that grows as it approaches,
139
+ leaving tyre tracks that fade. Why: sells the board's depth, which the oblique camera already has.
140
+
141
+ **9. Frogger crossing** — several agents crossing simultaneously in opposite lanes at different
142
+ speeds, some of which collide. Why: collisions we already compute (`cycleCrashes`).
143
+
144
+ **10. Q*bert hopper** — a small agent hopping cube-to-cube diagonally, each landing recolouring
145
+ the cube it lands on for a moment. Why: Q*bert's board *is* our board — an isometric stack of
146
+ cubes. It is the single most on-the-nose match in this list.
147
+
148
+ **11. Marble Madness roller** — a ball that obeys the skyline: rolls off high cubes toward low
149
+ ones, gathering speed. **Data-aware**: it drains toward the cheap transactions.
150
+
151
+ **12. Trials rider** — a two-wheeler that follows the height profile of one row, wheelie-ing over
152
+ the tall ones.
153
+
154
+ ### II. Formations — many agents moving as one
155
+
156
+ The thing our board has never shown: *coordinated* motion. All are cheap because the formation is
157
+ one transform over a sprite list.
158
+
159
+ **13. Space Invaders descent** — a rank-and-file grid of invaders stepping sideways, dropping a
160
+ row at the edge, accelerating as their numbers fall. They shoot; hit cubes flash and the invader
161
+ above dies. Why: the operator asked for flying ships and pixel art, and this is *the* pixel-art
162
+ formation. Our board is already a grid; the invaders are a grid above it.
163
+
164
+ **14. Galaga wing** — ships peel off a formation in Lissajous dives and rejoin. Why: the dive
165
+ curve is two sines — trivial — and it looks far more expensive than it is.
166
+
167
+ **15. Galaga tractor beam** — one ship stops over a tall cube, beams it up (the cube rises off the
168
+ board and vanishes), and a "captured" ghost of it flies in formation. **Data-aware**, and it uses
169
+ `lift`, which most effects ignore.
170
+
171
+ **16. Xevious bombing run** — a ship crossing at altitude dropping markers ahead of itself; each
172
+ marker detonates on the cube beneath a beat later. Why: the *lead* — the marker lands before the
173
+ bomb — creates suspense.
174
+
175
+ **17. 1942 barrel roll** — a squadron crossing with a roll animation, sprite-scaled.
176
+
177
+ **18. R-Type charge beam** — a ship crosses slowly charging (a growing bead), then releases a beam
178
+ that lights *an entire row* of cubes at once. Why: a long wind-up and a big release is a rhythm
179
+ none of our current effects have.
180
+
181
+ **19. Gradius Options** — a lead ship trailed by four satellites that follow its exact past path
182
+ on a delay. Why: the delayed-follow is four lines of code and reads as intelligence.
183
+
184
+ **20. Missile Command interception** — arcs rain from the top toward cubes; interceptors launch
185
+ from the bottom and detonate as expanding rings that stop them. Why: two agent populations that
186
+ interact, and the expanding-ring geometry is already in `drawCycles`' de-res.
187
+
188
+ **21. Asteroids drift** — wireframe polygons tumbling across, splitting into smaller ones when
189
+ they cross a tall cube. Why: pure vector art, which is exactly what this renderer draws natively.
190
+
191
+ **22. Zerg rush** — a swarm of small agents entering from one edge with slight per-agent noise,
192
+ converging on the tallest cube. Why: emergent-looking, trivially cheap, and **data-aware**.
193
+
194
+ **23. Lemmings procession** — a line of walkers that turn at obstacles, some digging *through* a
195
+ tall cube (which visibly shortens as they pass). Why: the board changing shape under an effect.
196
+
197
+ **24. Bomberman blast** — an agent walks the grid, drops a bomb, and the blast runs in four
198
+ straight lines until a tall cube stops it. Why: the cross-shaped blast *reads the skyline* as
199
+ walls — it is a picture of the board's density. **Data-aware**.
200
+
201
+ ### III. Boards that transform — the effect changes the board itself
202
+
203
+ **25. Tetris drop** — a tetromino falls onto the skyline, locks onto the tops of cubes, and if it
204
+ completes a flat row, that row flashes and clears. Why: we already have `tetris.js` rules, pure
205
+ and tested; this is that rule-set pointed at the block board.
206
+
207
+ **26. Breakout wall** — the top rows of cubes become bricks; a ball and paddle play a few seconds
208
+ of Breakout against them. Why: `breakout.js` exists and is pure.
209
+
210
+ **27. Qix / Gravitrons claim** — a line agent crawls the board's perimeter, cuts inward, and the
211
+ enclosed region is claimed (filled with colour). Why: an effect that *encloses* rather than
212
+ sweeps; nothing else here does area.
213
+
214
+ **28. Pipe Mania** — pipe segments lay themselves tile by tile into a connected run; then fluid
215
+ flows the completed pipe. Why: the two-phase build-then-flow is a strong rhythm.
216
+
217
+ **29. Minesweeper reveal** — a flood-fill reveal spreading from one cube, stopping at "mines"
218
+ (the highest-feerate transactions), which then flash red. **Data-aware**, and flood-fill from a
219
+ seed is a genuinely different spread shape from a radius.
220
+
221
+ **30. Boulder Dash collapse** — cubes unsupported by a neighbour fall, cascading. Why: uses the
222
+ skyline as physics.
223
+
224
+ **31. Katamari roll** — a ball that rolls over the board *absorbing* cubes, visibly growing.
225
+ **Data-aware**: it gets bigger the more weight it crosses.
226
+
227
+ **32. Dig Dug tunnel** — an agent tunnels *through* the board leaving a visible cleared corridor
228
+ that slowly refills.
229
+
230
+ ### IV. Scanners, beams and sweeps with an agent behind them
231
+
232
+ **33. Tempest lane pulse** — pulses rushing up lanes from a vanishing point toward the viewer.
233
+ Why: our oblique camera has a natural horizon; this is the only effect that uses the *depth* axis
234
+ as the axis of motion.
235
+
236
+ **34. Star Fox barrel corridor** — a moving frame of reference: the board appears to bank left and
237
+ right as an unseen ship rolls. Why: cheap (it is a camera trick) and startling.
238
+
239
+ **35. Rez / Child of Eden lock-on** — a reticle flicks across the board tagging eight cubes in
240
+ sequence, then fires eight homing streaks at all of them at once. Why: the tag-then-release
241
+ rhythm, and **data-aware** if it tags by feerate.
242
+
243
+ **36. Metroid scan visor** — a horizontal band that, as it passes, briefly *labels* the cubes it
244
+ crosses with their feerate. Why: an effect that makes the board more legible, not less — the only
245
+ one here that adds information.
246
+
247
+ **37. Sniper glint sweep** — a slow searchlight cone rotating from one corner.
248
+
249
+ **38. Portal pair** — two portals open on opposite sides; an agent enters one and exits the other,
250
+ carrying its trail through. Why: the trail crossing the discontinuity is a genuinely novel visual.
251
+
252
+ **39. Tron derez wave** — a front that de-resolutes cubes into shards as it passes and
253
+ re-resolves them behind it. We already have de-res shards in `drawCycles`.
254
+
255
+ **40. Matrix bullet-time orbit** — everything holds still while one agent orbits the board.
256
+
257
+ ### V. Sprites and pixel art — the operator's "flying ships"
258
+
259
+ `arkanoid.js` already proves this works: its capsules are multi-part glyph-on-body silhouettes
260
+ with `poly`, `rot` and `floor`, tumbling as they fall, and its enemies are drawn the same way.
261
+ A sprite here is a small list of tiles with offsets — not a bitmap — so it scales, rotates and
262
+ lights like everything else on the board.
263
+
264
+ **41. UFO flyby** (Space Invaders' mystery ship) — the saucer crossing the top of the board on a
265
+ timer, worth "points"; a rare visitor rather than a regular effect. Why: rarity is its own effect.
266
+
267
+ **42. Zaxxon isometric fighter** — a ship flying *at an altitude above the board*, with its shadow
268
+ tracking on the cube tops beneath it. Why: we already cast shadows; nothing currently flies.
269
+
270
+ **43. Choplifter rescue** — a helicopter that descends to a cube, hovers, lifts it, and carries it
271
+ off the board.
272
+
273
+ **44. Defender sweep + humanoid** — fast horizontal scroll, with a lander stealing a cube and a
274
+ ship intercepting it.
275
+
276
+ **45. Joust flap** — two winged riders in slow flapping arcs that collide; the higher one wins.
277
+
278
+ **46. Mario pipe warp** — a sprite descends into the board between two cubes and re-emerges
279
+ elsewhere.
280
+
281
+ **47. Rampage climb** — a large sprite that climbs the tallest stack and knocks the top cube off.
282
+
283
+ **48. Pixel-art banner drift** — a sprite spelled out in cubes (a block, a coin, a "GM") drifting
284
+ across as a formation of lit cells, then dispersing. Why: it turns the board itself into the
285
+ sprite — the highest-impact version of pixel art here.
286
+
287
+ **49. Duck Hunt flush** — birds break from the board and fly off; one is "shot" and falls back in.
288
+
289
+ **50. Konami code easter egg** — the code typed on the keyboard triggers a scripted sequence
290
+ (a 30-ship formation, or every effect at once for four seconds). Why: it is the arcade tradition
291
+ the whole board is quoting, and it costs nothing until someone finds it.
292
+
293
+ ---
294
+
295
+ ## Built so far
296
+
297
+ *(The record of 2026-09-13, as written that day. The removals described at the top of this
298
+ document happened afterwards; of the agents named below, only `centipede`, `tractor`, `missile`
299
+ and `boulderdash` remain, alongside `lightcycle`, `ball` and the later `stormball`.)*
300
+
301
+ Batch one landed 2026-09-13: **recognizer (1), disc (2), snake (3), qbert (10), invaders (13),
302
+ bomberman (24)** -- five different motion vocabularies, two of them data-aware.
303
+
304
+ Batch two, the same day: **centipede (4), pacman (5), galaga (14), tractor (15), missile (20),
305
+ asteroids (21)**. These add the three things batch one had no example of -- an agent that
306
+ DIVIDES (the centipede splits and both halves carry on), agents with DIFFERENT RULES FROM EACH
307
+ OTHER (four pursuers: chase, ambush, scatter, wander), and TWO POPULATIONS THAT INTERACT (arcs
308
+ raining down against interceptors rising to meet them). The tractor beam is the only agent that
309
+ uses `lift`, and it is data-aware: it takes the tallest transaction on the board and puts it back.
310
+
311
+ Fourteen agents now. `test/agents.test.js` holds every one of them to the same contract -- builds,
312
+ frames, draws, publishes heads, replays from a seed, and leaves the board's tiles untouched -- so a
313
+ new batch is checked against all of it the moment it is registered.
314
+
315
+ Batch three, the same day: **tetrisdrop (25), qix (27), minesweeper (29), tempest (33),
316
+ lockon (35), scanvisor (36)**. These work in AREA rather than along a path -- qix claims a region,
317
+ minesweeper floods outward and goes *around* what blocks it -- and two of them add INFORMATION
318
+ rather than decoration, which is the rarest thing an effect here can do: lockon tags the eight
319
+ richest transactions in turn before firing, and scanvisor labels the blocks it crosses with their
320
+ feerate. Three of the six are data-aware. `tetrisdrop` points the real, already-tested rules in
321
+ `tetris.js` at the skyline instead of a well, so the piece shapes are not reinvented.
322
+
323
+ Batch four, the same day: **katamari (31), boulderdash (30), lemmings (23), marble (11),
324
+ gradius (19), portal (38)**. This is the family that ALTERS the board, and it could not be built
325
+ until heads carried `hide`/`scale` through to the renderer -- see the fourth lesson below. The
326
+ katamari absorbs cubes as it rolls and grows with what it has taken; boulder dash collapses them
327
+ outward from a point; a lemming digs straight through one. All of it snaps back, by construction:
328
+ nothing touches a tile, the override just stops being computed. `marble` is data-aware in a new
329
+ way -- it obeys the board as TERRAIN, rolling greedily downhill, so it drains away from the big
330
+ transactions and shows which way the block leans.
331
+
332
+ Batch five, the same day: **sonic (6), frogger (9), xevious (16), pipemania (28), derez (39),
333
+ ufo (41)**. `sonic` is the second agent to use height as TERRAIN rather than as an obstacle -- it
334
+ launches off the tallest block in its row and arcs. `xevious` is the only effect where you see what
335
+ is about to happen: the marker lands a beat before the bomb. `derez` takes the board apart as it
336
+ passes and puts it back behind itself, which is the plainest demonstration that an effect can do
337
+ that without touching a tile. `ufo` is deliberately brief -- rarity is its whole design.
338
+
339
+ **Thirty-two agents, and with the twenty-four field effects that is FIFTY-SIX on the board.**
340
+ Every entry in this catalogue worth building has been built. (An earlier note here said fifty;
341
+ that was my arithmetic, not a count -- there were twenty-four fields, not eighteen.)
342
+
343
+ **A seventh lesson: a bias written when the list was short becomes a takeover when it grows.**
344
+ `scheduleFx` gave the light cycles a 50% head start on the first effect after the board came to
345
+ rest -- a reasonable flourish among nine effects, and an eighteenfold bias among fifty-six (measured:
346
+ 33.2% of picks against 1.8% for an even split). The block-space board re-lays on every pool refresh,
347
+ so that branch fired constantly and the operator saw light cycles and little else. Nothing guarded
348
+ it, which is why it survived four batches of new effects being added around it. `effects.test.js`
349
+ now replicates the scheduler's choice and fails if any kind takes more than twice an even share, or
350
+ if any kind is unreachable.
351
+
352
+ Related, and the same shape: the recognizer flipped a coin between marching along grid X or grid Y.
353
+ On this camera +10 in grid x moves 73.4 screen pixels horizontally and +10 in grid y moves **zero**
354
+ -- y is depth. So half the time both its legs projected to the same screen x, the gantry collapsed
355
+ into a single vertical line, and it read as a scanning artifact. Geometry that is symmetric in the
356
+ data is not necessarily symmetric on screen.
357
+
358
+ **A sixth lesson, and the one with the highest recurrence: on this board, FLAT IS THE COMMON CASE.**
359
+ The dense block-space viewer packs thousands of slabs at exactly the same height, so any agent that
360
+ reads the skyline for a gradient finds none. Three were blinded by it before the pattern was
361
+ obvious -- bomberman's wall threshold made every neighbour a wall and the blast drew a dot; marble's
362
+ greedy descent stopped at the first step and it sat still; sonic found no rise, so it never launched
363
+ and parked at the left edge. Each was measured only because its frame sizes stayed suspiciously
364
+ flat across a whole capture.
365
+
366
+ `agents.test.js` now plays EVERY registered agent on a perfectly uniform board and requires it to
367
+ keep publishing heads and to visit more than a handful of distinct places. A skyline-reading agent
368
+ must degrade to something worth watching, never to nothing.
369
+
370
+ **A fifth lesson, learned three times in one day: a measurement that contradicts a working picture
371
+ is usually the measurement.** Three times a probe reported an effect doing nothing while the
372
+ screenshot plainly showed it working. Every one announced itself the same way -- *results identical
373
+ across cases that should differ*, or a zero where a picture showed something:
374
+
375
+ - probing `hide` with `kind: 'katamari'`, which is not a registered kind, so every case fell to
376
+ `default: FX_NONE` and came back the same;
377
+ - sweeping the board-alterers over a 96x96 grid holding only 3,700 tiles -- 38 rows of 96 -- so the
378
+ agents roamed empty space and touched nothing. On a fully-populated board the same sweep reports
379
+ katamari hiding 2,173 cube-frames and boulder dash shrinking 3,404;
380
+ - judging invaders, bomberman and pacman from a four-phase capture of effects that run 6-9 s in
381
+ distinct phases.
382
+
383
+ Before believing a null result, check the experiment ran: does the control case behave differently?
384
+ Is the board the agent is walking actually occupied? Did the capture land inside the phase?
385
+
386
+ **A fourth lesson, and the sharpest one: machinery nobody uses is machinery that does not work.**
387
+ `hide` and `scale` were added to `fxAt`'s result so an effect could eat or collapse a cube and have
388
+ it snap back -- and the heads branch then hardcoded `hide: 0, scale: 1`, so no agent could actually
389
+ reach them. Three batches passed with that dead. It was only caught when batch four's whole family
390
+ (katamari, boulder dash, lemmings) was about to be built on top of it; every one of them would have
391
+ drawn a glow and called it eating. Wire a capability to a caller the same day you add it, or test
392
+ it end to end from the caller's side.
393
+
394
+ A third lesson, from verifying batch two: **a four-phase capture is not enough to judge an effect.**
395
+ Pac-Man was written up as "did not appear at all" on the strength of one frame; captured every
396
+ 500 ms instead, all five characters are plainly there -- the wedge mid-board with its mouth open
397
+ and four ghosts in the corners, eyes tracking him. The effect was fine and the *photograph* was
398
+ mistimed, which is the same mistake twice now (bomberman spends its first 35% walking). Capture
399
+ across the whole run, or do not draw a conclusion. They live in
400
+ `public/js/agents.js` behind the `AGENTS` registry described above; `test/agents.test.js` drives the
401
+ real build/frame/draw path for every registered kind.
402
+
403
+ Two things learned building them, which apply to all the rest:
404
+
405
+ - **Size in GRID UNITS, never in line-widths.** `lw` is about one device pixel, so a sprite drawn
406
+ at `lw * 20` is 20px on any board -- 2.8% of a 705px panel, a speck among 3,700 cubes. The first
407
+ cut of the recognizer, the invaders and the blast were all invisible for this reason and had to
408
+ be redrawn against `view.unit` (7.3px per unit on the dense 96-grid board, 16px on the 44-grid
409
+ one). `drawCycles` had it right all along with `wallH = 3`.
410
+ - **The board is never mutated.** `fxAt` results carry `hide` and `scale`, applied per frame onto a
411
+ *copy* of the tile. An effect interrupted by a transition or a hidden tab stops being computed
412
+ and the board is correct again by construction -- which is the only version of "it snaps back"
413
+ that cannot leak.
414
+
415
+ ## What I would build first, and why
416
+
417
+ *(The proposal as made before any were built. All six were built; of them only Missile Command
418
+ survived the operator's cull.)*
419
+
420
+ If the point is to blow people away rather than to add length to a list:
421
+
422
+ 1. **Space Invaders descent (13)** — the formation is the thing our board has never done, and it
423
+ is instantly recognisable to everyone who will look at a screenshot.
424
+ 2. **Q*bert hopper (10)** — the closest match between a real arcade board and ours; it will look
425
+ *native*, not bolted on.
426
+ 3. **Recognizer (1)** — solves the light cycles' real weakness (too thin on a dense board).
427
+ 4. **Bomberman blast (24)** — the first genuinely **data-aware** agent: the blast is stopped by
428
+ the skyline, so the picture it draws is a picture of the mempool's shape.
429
+ 5. **Snake (3)** — eats the biggest transactions first, and its own length is the readout.
430
+ 6. **Missile Command (20)** — two interacting populations; the most "alive" of the lot.
431
+
432
+ That is six effects covering all five families, each with a different motion vocabulary. Beyond
433
+ that the list has more ideas than the board has seconds to show them: at one effect every 5-9 s
434
+ plus its own run time, thirty kinds means any given one appears a few times an hour.
435
+
436
+ ## Costs and constraints to respect when building these
437
+
438
+ - **The dense board is 96 units across** with thousands of slabs. An agent that touches every cube
439
+ per frame is O(tiles) per frame; the heads branch in `fxAt` is already that, so keep the
440
+ *number of heads* small (the light cycles use two).
441
+ - **Determinism**: board-level choices come from `fxHash(seed)`, never `Math.random()`, in
442
+ anything the tests replay. Per-frame sparkle (the crackle) may be random because it is never
443
+ asserted frame-to-frame.
444
+ - **Every effect needs a switch** in `settings.js` `effects` and a row in `PANEL`, in `FX_KINDS`
445
+ order, or `effects.test.js` fails. That is a feature: it is what stops an effect shipping
446
+ without a way to turn it off.
447
+ - **Sprites** should be built as tile lists like `capsuleTiles()`, so they light, shadow and
448
+ depth-sort with everything else rather than being painted over the top.
@@ -0,0 +1,205 @@
1
+ # Getting started: macOS or Linux, from a command prompt
2
+
3
+ Everything here is typed into a terminal on **the machine that runs Bitcoin Core**. BlockYard
4
+ reads the node's block files to build the explorer's address index, so it runs on the node's
5
+ machine and nowhere else. Ten minutes of typing, then the index build runs on its own.
6
+
7
+ ## 1. Bitcoin Core
8
+
9
+ BlockYard needs Bitcoin Core **25.0 or later** (`getblock` verbosity 3, which the index follower
10
+ uses; v29 is what it is being released against), with its RPC server on and a transaction index.
11
+ Add to `bitcoin.conf` if they are not there, and restart the node:
12
+
13
+ ```conf
14
+ server=1
15
+ txindex=1
16
+ ```
17
+
18
+ `txindex=1` on a node that has run without it triggers a one-off reindex that takes a while;
19
+ `bitcoin-cli getindexinfo` says `"synced": true` when it is done (with the macOS app bundle,
20
+ `bitcoin-cli` is inside it: `/Applications/Bitcoin-Qt.app/Contents/MacOS/bitcoin-cli`). Everything but the explorer's
21
+ transaction-by-id pages works before that. `coinstatsindex=1` is optional: without it the Chain
22
+ page's UTXO figures are blank and the node is not asked for them.
23
+
24
+ **Disk:** the address index is about **125 GB**, on top of the node's own ~875 GB of block
25
+ files; the build reads those files once. A different disk from the node's is best, if there is one.
26
+
27
+ Where `bitcoin.conf` and the data directory are, by default:
28
+
29
+ | | data directory | `bitcoin.conf` |
30
+ |---|---|---|
31
+ | macOS | `~/Library/Application Support/Bitcoin` | the same directory |
32
+ | Linux | `~/.bitcoin` | the same directory |
33
+
34
+ A **pruned** node will not do: the index needs every block file.
35
+
36
+ ## 2. Node.js 22 or newer
37
+
38
+ ```bash
39
+ node -v # v22.x or later
40
+ ```
41
+
42
+ If that prints nothing or an older version:
43
+
44
+ - **macOS:** `brew install node` (Homebrew's current Node is newer than 22), or the installer
45
+ from <https://nodejs.org>. (`node@22` from Homebrew is keg-only and needs linking; the plain
46
+ `node` formula is simpler.)
47
+ - **Linux:** your distribution's `nodejs` package if it is 22+, otherwise the installer or
48
+ NodeSource repository from <https://nodejs.org>.
49
+
50
+ ## 3. Get BlockYard
51
+
52
+ ```bash
53
+ git clone https://github.com/BobClawblaw/blockyard.git
54
+ cd blockyard
55
+ npm test # optional: the unit tests; there is no npm install -- no dependencies
56
+ ```
57
+
58
+ ## 4. `npm run setup` — the installer
59
+
60
+ ```bash
61
+ npm run setup
62
+ ```
63
+
64
+ It asks for the data directory first and **reads the node's own `bitcoin.conf`** there -- the
65
+ chain, `rpcport`, `rpcconnect`, `rpcuser`/`rpcpassword`, `rpcauth` users, a cookie file the node
66
+ was told to write elsewhere, `server=` and `txindex=`, sections (`[main]`, `[test]`, ...) and
67
+ `includeconf=` all understood -- so the rest arrive as defaults to accept rather than questions
68
+ to answer. Then it checks the answers against the node (every call timed, a verbose mempool read
69
+ included, so you learn how fast the node answers before anything depends on it), asks where the
70
+ web interface and the index go, writes `config/local.json`, and offers to build the address
71
+ index. Every check is a read; nothing on the node is changed. The banner is pixel art and, like
72
+ everything the installer prints, fits an 80-column terminal.
73
+
74
+ | it asks | default | what it does with the answer |
75
+ |---|---|---|
76
+ | Bitcoin Core data directory | the platform default above | reads `bitcoin.conf`; finds the `.cookie` for RPC authentication, the `blocks/` directory, and `debug.log` |
77
+ | Bitcoin Core RPC URL | from `bitcoin.conf`: `rpcconnect` and `rpcport`, else `http://127.0.0.1:8332` (the chain's default port) | connects, reads the chain and height |
78
+ | a label | `Bitcoin Core` | what the header calls the node |
79
+ | rpcUser / rpcPassword | from `bitcoin.conf` when it has them; *asked only if no cookie is readable* -- an `rpcauth` user is pre-filled, its password is not in the file | a node using `rpcauth` instead of the cookie |
80
+
81
+ Then it prints the checks. This is what a good node looks like:
82
+
83
+ ```
84
+ 2/6 Checking the node ────────────────────────────────────────────────────
85
+
86
+ ✓ credentials cookie /Users/you/Library/Application Support/Bitcoin/.cookie
87
+ ✓ rpc http://127.0.0.1:8332 answers in 10 ms: chain main, block 967,016 of 967,016 headers
88
+ ✓ version /Satoshi:29.1.0/ (290100)
89
+ ✓ txindex synced to 966,978
90
+ · coinstatsindex off: the Chain page marks UTXO figures unindexed (optional)
91
+ ✓ getblock 3 the tip block decodes with prevouts (4,077 transactions) in 921 ms
92
+ ✓ mempool 34,455 transactions, verbose, in 1.0 s
93
+ · address index rpc the node has no address index, as expected of Bitcoin Core; BlockYard builds its own
94
+ ✓ block files 5757 block files and 5757 undo files, 875.9 GB, XOR-obfuscated (xor.dat present)
95
+ ✓ read a block blk00000.dat opens and its first record is the genesis block
96
+ · node log /Users/you/Library/Application Support/Bitcoin/debug.log (10.3 MB) -- found; not parsed on Bitcoin Core, and not needed
97
+
98
+ everything this needs is there
99
+ ```
100
+
101
+ A ✗ names what is missing and what to do about it (`txindex=1`, a readable cookie, the chain
102
+ the node is really on, a pruned node). A slow `getblock 3` (5 s or more) or a slow mempool read
103
+ (10 s or more) is a warning, not a failure: it says the board and the block being built will lag
104
+ behind the node. If the RPC server does not answer it offers to ask again; with anything else
105
+ failing it asks before writing. Every answer is validated before it is accepted -- a URL that is
106
+ not one, a port outside 1-65535, a directory that is not there -- and asked again.
107
+
108
+ Then, in this order:
109
+
110
+ | it asks | default | notes |
111
+ |---|---|---|
112
+ | bind address | `127.0.0.1` | keeps it to this machine; `0.0.0.0` opens it to everyone who can reach the port, see [SECURITY.md](SECURITY.md) |
113
+ | port | `21000` | it warns if something (a running BlockYard included) already answers there |
114
+ | index directory | `data/index` inside the checkout | alongside everything else this install writes; about 125 GB for the whole chain -- give another path to put it on a different disk from the node's |
115
+ | build workers | `4` (fewer on a machine with fewer cores or less memory; never more by default) | each needs about 2.5 GB of memory. **Answer 1 if the block files are on spinning disks**: parallel readers only seek against each other and against the node, and the build takes hours there whatever you answer |
116
+
117
+ It writes `config/local.json` (mode 0600; a backup is kept if one was there) and shows it. The
118
+ number of workers is written as `addressIndexWorkers`, so the background build uses the same one.
119
+
120
+ **Step 6, building the index** — the default is **(b)ackground**: BlockYard builds it itself
121
+ once it starts, on worker threads, while every page keeps working. **Expect the build to take a few hours** — about two on the installer's default of four workers on NVMe, longer on spinning disks — during which every other page works and address pages show the build's progress in place of a history. The Overview's "What this
122
+ panel cannot tell you" box shows the progress (`the address index is being built: scan 1,234 of
123
+ 5,757 (21%), 1,204,511,033 rows so far, about 20 min left`), the address page says the same in
124
+ place of a history, and a notification pops up at the start and when it is done (both appear in
125
+ the events feed, as does a failure) — the address pages fill in from then on, no restart. The
126
+ ETA settles after the first few files. The build is **paced by the node**: it holds while the
127
+ node's RPC is failing or answering slower than the monitor's slow threshold (`rpc.slowLatencyMs`,
128
+ 5 s by default), eases off while merely slow, and says so in the progress line and the log; it
129
+ talks to the node over its own RPC connection so the pages are not queued behind it. Reading
130
+ every block file once took **29 min 45 s on 16 workers** on NVMe on the machine it was measured
131
+ on; expect roughly four times that on four. **(h)ere** builds it in this terminal instead, with a
132
+ progress bar and the same pacing; **(l)ater** writes `addressIndexBuild: "manual"` so nothing
133
+ builds until you run this and restart BlockYard:
134
+
135
+ ```bash
136
+ node scripts/index-build.js --out data/index --workers 4
137
+ ```
138
+
139
+ Last question: **start BlockYard now, in this terminal?** — yes runs it right there (Ctrl-C
140
+ stops it, and stops a background build with it; there is no resume, so it starts over on the
141
+ next start); `--start` does the same without asking.
142
+
143
+ Scripted, with no questions (a fresh machine, a Makefile):
144
+
145
+ ```bash
146
+ node scripts/setup.js --yes --rpc-url http://127.0.0.1:8332 \
147
+ --datadir "$HOME/Library/Application Support/Bitcoin" --index-dir "$PWD/data/index" --workers 4
148
+ ```
149
+
150
+ Every question has a flag: `--datadir`, `--rpc-url`, `--label`, `--rpc-user` / `--rpc-password`,
151
+ `--host`, `--port`, `--index-dir`, `--workers`. `--build-here` builds in the terminal and
152
+ `--build-later` leaves it to you; `--start` boots the monitor at the end; `--force` replaces an
153
+ existing `config/local.json` (with a backup).
154
+
155
+ ## 5. Run it
156
+
157
+ ```bash
158
+ npm start
159
+ ```
160
+
161
+ The log says `BlockYard 0.1.0 listening on https://127.0.0.1:21000` (its own self-signed
162
+ certificate, made on this first start; the browser warns once and remembers it), then `created the
163
+ first admin account (admin)` with a generated password **shown once** — copy it, or set
164
+ `BLOCKYARD_ADMIN_PASSWORD` before the first start to choose it — then `address index: building
165
+ /Users/you/blockyard/data/index from main's block files with 4 workers -- the Overview shows the
166
+ progress` (and `address index build: paused while the node's RPC is answering in … s` /
167
+ `resumed` if the node struggles) and, when that is done, `address index built: … rows to block N
168
+ in … min -- address pages are live` followed by `address index /Users/you/blockyard/data/index:
169
+ following main from block N`. Open <https://127.0.0.1:21000> and sign in as `admin`. The
170
+ Overview fills in within about thirty seconds; Block space lands a little after. Open Explorer,
171
+ click the latest block, then any output address: while the index is building the page says so
172
+ with the progress; once it is built, its balance, history and unspent outputs appear.
173
+
174
+ To check the setup again at any time, or after changing the node:
175
+
176
+ ```bash
177
+ npm run check
178
+ ```
179
+
180
+ It runs the same checks against every node in `config/local.json`, with every call timed, and
181
+ exits non-zero if one fails, so it can sit in a health script.
182
+
183
+ ## 6. Keep it running
184
+
185
+ - **Linux:** as a systemd service — [INSTALL.md §6](INSTALL.md#6-run-it-as-a-service).
186
+ - **macOS:** leave `npm start` running in a terminal (or a `tmux`/`screen` session), or write a
187
+ `launchd` plist that runs `node server/main.js` in the checkout with `KeepAlive`.
188
+
189
+ ## 7. Updating
190
+
191
+ ```bash
192
+ git pull
193
+ npm test
194
+ ```
195
+
196
+ then restart it. There is nothing to install and no build step. If a release says the index
197
+ format changed, delete the index directory (or rebuild it with the same `index-build.js` command
198
+ into the same directory — the build empties the directory first) and BlockYard builds it again
199
+ on the next start.
200
+
201
+ ## If something does not fill in
202
+
203
+ [TROUBLESHOOTING.md](TROUBLESHOOTING.md): a node showing offline, an empty block-space board,
204
+ a slow or paused index build, STALLED in the header, a transaction id not found (`txindex`), an
205
+ address page reading *not indexed* or *behind*, no dollar figures, a slow board.