blockyard 0.1.0 → 0.1.2

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 (71) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/README.md +13 -11
  3. package/SECURITY.md +2 -2
  4. package/docs/API.md +1 -1
  5. package/docs/ARCHITECTURE.md +36 -5
  6. package/docs/CONFIGURATION.md +6 -4
  7. package/docs/DEFECTS.md +4 -1
  8. package/docs/GETTING-STARTED.md +14 -7
  9. package/docs/INSTALL.md +7 -4
  10. package/docs/PLAN-SCORCHED-YARD.md +456 -0
  11. package/docs/PLAN-SKIES.md +142 -0
  12. package/docs/SECURITY-AUDIT-2026-09-16.md +647 -0
  13. package/docs/SECURITY.md +26 -7
  14. package/docs/TROUBLESHOOTING.md +10 -5
  15. package/docs/USER-GUIDE.md +247 -9
  16. package/package.json +4 -2
  17. package/public/css/app.css +87 -0
  18. package/public/index.html +58 -6
  19. package/public/js/app.js +60 -19
  20. package/public/js/blockanoid.js +15 -7
  21. package/public/js/blockout.js +15 -7
  22. package/public/js/blockscene3d.js +51 -11
  23. package/public/js/depthchart.js +1 -1
  24. package/public/js/details3d.js +25 -2
  25. package/public/js/explorer.js +7 -1
  26. package/public/js/livingsky.js +494 -0
  27. package/public/js/login.js +3 -2
  28. package/public/js/mining.js +4 -4
  29. package/public/js/panels.js +27 -18
  30. package/public/js/safenext.js +14 -0
  31. package/public/js/scorched.js +1071 -0
  32. package/public/js/scorchedai.js +268 -0
  33. package/public/js/scorchedair.js +286 -0
  34. package/public/js/scorchedfx.js +376 -0
  35. package/public/js/scorchedshop.js +105 -0
  36. package/public/js/scorchedwind.js +69 -0
  37. package/public/js/scorchedyard.js +1361 -0
  38. package/public/js/settings.js +266 -80
  39. package/public/js/tetrust.js +15 -6
  40. package/public/js/tetsound.js +35 -5
  41. package/scripts/check.js +46 -0
  42. package/scripts/index-build.js +9 -2
  43. package/scripts/pool-map.js +152 -36
  44. package/scripts/setup.js +108 -10
  45. package/scripts/shots.mjs +27 -0
  46. package/scripts/smoke.sh +6 -5
  47. package/scripts/ui.js +4 -2
  48. package/server/auth/sessions.js +33 -13
  49. package/server/chain/blockfile.js +64 -5
  50. package/server/chain/index/build.js +432 -56
  51. package/server/chain/index/heights.js +29 -3
  52. package/server/chain/index/live.js +13 -7
  53. package/server/chain/index/rows.js +6 -1
  54. package/server/chain/index/store.js +28 -5
  55. package/server/chain/index/worker.js +23 -11
  56. package/server/collect/logparse.js +65 -18
  57. package/server/collect/markets.js +76 -7
  58. package/server/collect/mining.js +32 -0
  59. package/server/collect/monitor.js +24 -11
  60. package/server/collect/network.js +19 -9
  61. package/server/config.js +7 -0
  62. package/server/http/api.js +70 -13
  63. package/server/http/server.js +22 -5
  64. package/server/http/sse.js +53 -7
  65. package/server/main.js +13 -3
  66. package/server/rpc/allowlist.js +26 -0
  67. package/server/rpc/client.js +30 -2
  68. package/server/store/audit.js +6 -1
  69. package/server/store/history.js +19 -3
  70. package/server/store/ledger.js +15 -4
  71. package/systemd/blockyard.service +34 -3
package/docs/SECURITY.md CHANGED
@@ -23,8 +23,9 @@ someone using the monitor to make the node do something (prevented: writes are o
23
23
  console is read-only behind a default-deny allowlist), someone reading your node's state who
24
24
  should not (controlled by where it listens, a CIDR gate, and optional accounts), and the
25
25
  monitor leaking information about you to third parties (limited to the on-demand market data
26
- connections listed below, which you can turn off). The monitor does not hold keys and has no
27
- wallet access.
26
+ connections listed below, which you can turn off). The monitor does not hold keys, and every
27
+ wallet RPC is refused by name (since 2026-09-16; before that, wallet reads such as
28
+ `listdescriptors` passed the allowlist's `list` prefix, see the audit of that date).
28
29
 
29
30
  ## Access: sign-in by default, open on request
30
31
 
@@ -46,6 +47,7 @@ Open access is still available as a posture you choose: with `auth.enabled: fals
46
47
  | peer, mempool, block and transaction detail, the explorer | the audit trail |
47
48
  | the read-only RPC console (behind the allowlist) | password changes and sessions |
48
49
  | the live Server-Sent Events stream | **every node write**, even if actions are enabled |
50
+ | Display settings (they are normalised before use) | the node connection form, except from this machine's loopback address (`auth.openNodeConfigFromNetwork` widens it) |
49
51
 
50
52
  The `viewer` ceiling cannot be raised by configuration or by any credential while accounts
51
53
  are off. The start-up log states which addresses are readable and how to close them, so
@@ -78,19 +80,30 @@ The last enabled admin cannot be demoted, disabled or deleted.
78
80
  routes had no cross-site protection at all. An audit proved it with a working exploit against
79
81
  the node-connection test. Open mode now refuses any state-changing request whose `Origin` is
80
82
  not this server, or whose `Sec-Fetch-Site` says cross-site. A client that sends neither header
81
- (curl, a script) is unaffected: it can already reach the port, and this guards against what a
82
- *browser* can be made to do on someone's behalf.
83
+ (curl, a script) is not stopped by that check, which only guards against what a *browser* can be
84
+ made to do. So the one form where a script could do real harm, the node connection (a saved
85
+ address decides where the node's cookie goes after a restart, and its test makes the server
86
+ connect somewhere), answers only a loopback caller while accounts are off (audit 2026-09-16).
87
+ A save that moves the node to a different host drops the old endpoint's `rpcUser`,
88
+ `rpcPassword` and `cookieFile`, and a test of a foreign endpoint reports the kind of failure,
89
+ never what the endpoint answered.
83
90
  - **Brute force**: sign-in is locked after 8 failures in 5 minutes per username and per
84
91
  address, for 10 minutes, with the same error and the same hashing time for unknown users
85
92
  and wrong passwords. A separate throttle limits sign-in attempts per address, because each
86
93
  attempt costs a deliberately expensive hash.
87
94
  - **Rate limits** apply per client address.
95
+ - **Slow and stalled clients** cannot hold memory or connections: a request, body included, must
96
+ arrive within 30 seconds; an event stream whose reader stops reading is sent nothing more until
97
+ it drains, and is dropped when 4 MB is buffered or it stays blocked for a minute; one address
98
+ (or account) holds at most 16 streams. Free-form audit fields are clamped to 1,024 characters,
99
+ so a caller cannot rotate real events out of the trail.
88
100
 
89
101
  ## Talking to the node
90
102
 
91
103
  - **Read-only allowlist, default deny.** The RPC console and every internal call go through
92
- an allowlist: read-shaped methods are allowed; wallet, key-material, spending, peer-control,
93
- chain-mutating and very heavy methods are refused by name — including `getnewaddress` and
104
+ an allowlist: read-shaped methods are allowed; every wallet method (reads included, since some
105
+ return private keys), spending, peer-control, chain-mutating and very heavy methods are refused
106
+ by name — including `getnewaddress` and
94
107
  `getrawchangeaddress`, which start with "get" but create keys. Unknown methods are refused.
95
108
  - **One request at a time.** The node's RPC server is single-threaded, so the monitor runs a
96
109
  single serialized request lane with a minimum spacing, batching, priorities and a stale-drop
@@ -202,7 +215,13 @@ and errors; they never contain passwords, session tokens or RPC credentials.
202
215
  ## Hardening checklist
203
216
 
204
217
  - Run as a dedicated, unprivileged account that can read only the node's cookie and its
205
- `blocks/` directory (and log), and write only its own `data/` and the index directory.
218
+ `blocks/` directory (and log), and write only its own `data/` and the index directory. The
219
+ shipped systemd unit enforces the write half (`ProtectSystem=strict` with `ReadWritePaths=` for
220
+ `data/`, `config/` and the index) and drops capabilities, system calls and address families the
221
+ monitor does not use; add your index directory to it.
222
+ - Point `addressIndex` (or `--out`) at a new or empty directory. The build refuses a symlink, the
223
+ filesystem root, a home or working directory, the node's blocks directory, and any directory
224
+ holding files an index does not write; it only ever removes its own files.
206
225
  - Bind the narrowest set of addresses that serves your users; add a firewall rule if needed.
207
226
  - Turn accounts on if anyone who can reach the port should not see your node.
208
227
  - Use HTTPS, a reverse proxy, or an SSH tunnel on untrusted networks.
@@ -168,17 +168,22 @@ so far and an ETA; the address page repeats it. Things it says, and what they me
168
168
  2026-09-15 that fails the build at once with `an index worker exited with code N while on
169
169
  {"type":"scan","file":...}` and the flag turns to *build failed*; before that fix the build
170
170
  hung there for good. Either way: restart BlockYard with fewer workers (`addressIndexWorkers` on
171
- the node entry in `config/local.json`). The build starts over; it is not resumable. The flag
171
+ the node entry in `config/local.json`). The build resumes with the files it had finished. The flag
172
172
  also says **no progress for N min** whenever nothing has moved for two minutes, so a stall is
173
173
  visible as one rather than as a stale ETA.
174
174
  - **Hours, not minutes** — expect **a few hours**: 29 min 45 s is 16 workers on NVMe, and four
175
175
  workers (the installer's default) are roughly four times slower; **spinning disks** are slower still whatever the
176
176
  number, and there one worker is the fast setting, because parallel readers only seek against
177
177
  each other and against the node. Set `addressIndexWorkers` on the node entry in
178
- `config/local.json` (the installer writes the number you gave it) and restart: there is no
179
- resume, so the build starts over from the first file.
180
- - **It started over** — stopping BlockYard stops the build, and the next start begins it again
181
- from scratch. Leave it running until the notification says it is done.
178
+ `config/local.json` (the installer writes the number you gave it) and restart: the build resumes with the files already scanned, and only the rest are read with the
179
+ new number of workers.
180
+ - **It started over** — a stopped build normally resumes: the log says `address index build:
181
+ resuming the interrupted build in <dir> at block N` with how much was already done. When it
182
+ says `discarding the interrupted build … and starting over` instead, the reason follows: the
183
+ node's chain reorganised below the block the build was working to, the node is behind it (a
184
+ reindex, or another node), the index format changed with an update, or the journal or a bucket
185
+ file was damaged. Unfinished work that cannot be proven whole is never used, so the build begins
186
+ again from the first file.
182
187
  - **the address index build failed** — the reason is in the events feed and the log; fix it and
183
188
  restart (the server builds again), or run `node scripts/index-build.js --out <dir>` by hand.
184
189
  A pruned node, unreadable block files and a full disk are the usual causes.
@@ -20,6 +20,7 @@ are node actions, which are off unless an operator explicitly enables them (see
20
20
  - [Tetrust](#tetrust)
21
21
  - [Blockout](#blockout)
22
22
  - [Blockanoid](#blockanoid)
23
+ - [Scorched Yard](#scorched-yard)
23
24
  - [Wolfenstein 3D](#wolfenstein-3d)
24
25
  - [DOOM](#doom)
25
26
  - [Quake](#quake)
@@ -408,8 +409,8 @@ page reads **not indexed** and says so in a note with the phase, the progress, t
408
409
  and the time left; the Overview's "what this panel cannot tell you" box shows the same line, and
409
410
  adds `paused while the node's RPC is slow` whenever the build is holding back so the node keeps
410
411
  answering. A notification appears in every open tab when the build starts, when it finishes and
411
- if it fails; on finish the page fills in with no restart. The build does not resume after the
412
- server is stopped: the next start begins it again.
412
+ if it fails; on finish the page fills in with no restart. A build stopped with the server
413
+ resumes on the next start from the files it had finished, and its progress picks up from there.
413
414
 
414
415
  **Without an index configured**, the address and its type are still confirmed
415
416
  (`validateaddress` needs none), and balance, totals and history read **not indexed**. It does
@@ -717,6 +718,203 @@ rather than the look, flipping them reaches the game you are playing, not just t
717
718
 
718
719
  ---
719
720
 
721
+ ## Scorched Yard
722
+
723
+ Scorched Earth, the 1991 DOS artillery game, on the block engine: a landscape of dirt cubes that
724
+ craters and falls, tanks that aim by angle and power, wind, and a round that ends when one tank is
725
+ left. Under **Diversions**, after Blockanoid. You play against two computer opponents out of the
726
+ box (**Display settings → Scorched Yard → Computer players** fields up to seven, for the original's
727
+ eight seats). Every personality has its own colour, so you can see who you are up against; a second
728
+ or third of one kind is a lighter or darker shade of it.
729
+
730
+ The rules are the original's (docs/PLAN-SCORCHED-YARD.md is the whole plan): the manual's roster
731
+ of thirty-three weapons and eleven accessories at the manual's prices, packs and blast radii,
732
+ craters, falling dirt, fall damage, death and its blast, the walls, wind, cash for damage and
733
+ kills, a shop between rounds, and a game of rounds. The manual's eight computer
734
+ personalities take the other seats. The fabulous part is on its way.
735
+
736
+ ### Playing
737
+
738
+ | Keys | |
739
+ |---|---|
740
+ | **←** **→** or **A** **D** | the barrel's angle, a degree at a time (Shift: five) |
741
+ | **↑** **↓** or **W** **S** | power, ten at a time (Shift: one, Ctrl: a hundred) |
742
+ | **[** **]** or **PgUp** **PgDn** | the weapon, through what you own |
743
+ | **space** or **Enter** | fire |
744
+ | the mouse | drag on the field to aim: the direction from your tank is the angle, the distance the power |
745
+ | **A** **D** | drive a cell left or right, a unit of fuel each |
746
+ | **B** | a battery: 30 health back |
747
+ | **S** | raise a shield (the best you own) |
748
+ | **T** **H** | arm a contact trigger or heat guidance for the next shot |
749
+ | **P** or **Esc** | pause |
750
+ | **N** | the next round, from the shop |
751
+
752
+ ### The roster
753
+
754
+ You start with the bottomless **Baby Missile** and your cash; everything else is bought. What a
755
+ shell does when it lands:
756
+
757
+ | weapon | what it does |
758
+ |---|---|
759
+ | Baby Missile, Missile, Baby Nuke, Nuke | a blast: a crater and damage that falls off with distance, from a cell and a half across to eleven |
760
+ | Leap Frog | three warheads, one after another, each blast bigger than the last |
761
+ | Funky Bomb | a blast, then six bomblets thrown out that each blast where they land |
762
+ | MIRV, Death's Head | five (or nine, large) warheads splitting at the top of the arc |
763
+ | Tracer, Smoke Tracer | no crater and no damage: it shows you the wind |
764
+ | Baby Roller, Roller, Heavy Roller | lands and rolls downhill until it meets a tank or the bottom of a dip, then blasts |
765
+ | Riot Charge, Riot Blast | a wedge of dirt cut from the turret along the barrel's line, at once; nobody hurt |
766
+ | Riot Bomb, Heavy Riot Bomb | a shell that clears a sphere of dirt and hurts no one |
767
+ | Dirt Clod, Dirt Ball, Ton of Dirt | a shell that bursts into a ball of dirt |
768
+ | Liquid Dirt | oozes downhill and sets where it pools, filling the holes |
769
+ | Dirt Charge | a wedge of dirt thrown from the turret, at once |
770
+ | Earth Disrupter | every hanging piece of dirt on the field settles, at once |
771
+ | Napalm, Hot Napalm | liquid fire that runs downhill along the surface and burns what it reaches |
772
+ | Baby Digger, Digger, Heavy Digger | digs straight down, then blasts |
773
+ | Baby Sandhog, Sandhog, Heavy Sandhog | tunnels on along its heading through the dirt, then blasts |
774
+ | Plasma Blast | energy thrown from your own tank, its reach set by the power; you are spared, no crater |
775
+ | Laser | a straight line from the barrel, at once: dirt along it goes, a tank on it burns |
776
+
777
+ The accessories: **Shield**, **Force Shield** and **Heavy Shield** absorb 60, 100 and 150 damage;
778
+ a **Mag Deflector** pushes passing shells away and a **Super Mag** harder and from further; a
779
+ **Parachute** opens when you fall; a **Battery** restores health; **Auto Defense** raises a shield
780
+ for you as your turn begins; a **Fuel Tank** lets you drive; a **Contact Trigger** sets the shell
781
+ off within reach of a tank before it buries; **Heat Guidance** bends the shell toward the nearest
782
+ tank as it falls. The items line under the fire button shows what you carry and what is armed.
783
+ Prices, packs and radii are the manual's (SCORCH.DOC); the original started every player with $0
784
+ and 5% interest, which the settings allow.
785
+
786
+ ### The computer players
787
+
788
+ ![Scorched Yard](images/scorched.jpg)
789
+
790
+ Out of the box you face a **mix**: every new game deals a Shooter, a Tosser, a Chooser, a Spoiler,
791
+ a Cyborg and a Poolshark in a fresh order, so two opponents are a different pair each time, and up
792
+ to six opponents are all different kinds (a seventh starts a new deal). **Display settings → Scorched Yard
793
+ → Their kind** fields one kind in every seat instead. They are the manual's eight, and they
794
+ behave as it says:
795
+
796
+ | kind | colour | how it plays |
797
+ |---|---|---|
798
+ | Moron | yellow | picks an angle and a power, and shoots; buys at random |
799
+ | Shooter | blue | takes low, straight shots when it has a line of fire, and shoots like a Moron when it does not; its aim is rough at first and closes in on the same target |
800
+ | Poolshark | cyan | a Shooter, except that under rubber or spring walls it looks for the bank shot too |
801
+ | Tosser | green | lobs high, then corrects its power from each miss, by halves, until it hits |
802
+ | Chooser | purple | tries the straight shot, the lob and the bank, and takes whichever lands closest; a steadier hand than the Shooter's, not a perfect one |
803
+ | Spoiler | red | works the shot out from the wind and gravity and is nearly perfect |
804
+ | Cyborg | pink | a Spoiler with a grudge: it fires at whoever hit it last, else at the weakest, else at the leader |
805
+ | Unknown | grey | one of the others, drawn afresh each round and never announced |
806
+
807
+ How often each lands its first shot at a target, measured over hundreds of simulated games: the
808
+ Moron and the Tosser about one in twenty, the Shooter and the Poolshark about one in four, the
809
+ Chooser about two in five, and the Spoiler and the Cyborg about four in five. The Shooter, the
810
+ Poolshark, the Chooser, the Spoiler and the Cyborg aim better with each shot at the same target and
811
+ start afresh each round; the Tosser corrects in its own way. You are orange.
812
+
813
+ All but the Moron use a battery when hurt and raise a shield before firing, and each shops to its
814
+ own taste between rounds: Shooters buy missiles, Tossers MIRVs and Funky Bombs, Cyborgs the
815
+ Death's Head and the heaviest shields.
816
+
817
+ ### Cash and the shop
818
+
819
+ Every point of damage you land earns $10, a kill $2,000, and the last tank standing $1,000; interest
820
+ (a setting, 5% out of the box) is paid on what you keep between rounds. When a round ends the
821
+ **shop** opens: every weapon and item with its price and pack size, what you own, and a buy button
822
+ while you can afford it. The computer players shop at the same moment, in their own way. What you
823
+ buy stays with you for the rest of the game.
824
+
825
+ ### The controls
826
+
827
+ **← →** turn the barrel, **↑ ↓** change the power, and a **held key accelerates**: one a step, then
828
+ two, then five, so crossing the whole arc takes about a second and a half and the last degree still
829
+ takes one press. **Shift** is fine (1° and 1), **Ctrl** coarse (100 of power). **, and .** nudge the
830
+ power by one. The **wheel** over the field changes the power, and with **Shift** the angle. **Click
831
+ the angle or the power** on the panel and type the number. **Drag** on the field to aim; **press on
832
+ your own tank** and drag to turn the barrel without touching the power. **R** fires the last shot
833
+ again, exactly.
834
+
835
+ **Correcting.** Click an enemy tank to mark it (a red ring goes under it; click it again to clear).
836
+ After your next shot the panel says how it did against that tank — *10 over*, *6 short*, *close*,
837
+ *hit* — and **C** sets your power to what that miss says it should have been. It works from your
838
+ own last shot, so the wind and the hills are still yours to read; change the angle and it is only
839
+ an estimate. Display settings → Scorched Yard → *Correction helper* turns it off.
840
+
841
+ **Weapons.** **W** opens a grid of what you own, biggest blast first, with counts and the key for
842
+ each; click one or press its number. **1–9** pick the weapons you own in the shop's order, **Q**
843
+ picks the last one you fired, **[ ]** still cycle. The **fire button names what it will send**
844
+ (*fire · Missile × 5*), and the last of a weapon sold one at a time — a Nuke, a Death's Head —
845
+ **asks once**: the button turns red and says so, and a second press within three seconds sends it
846
+ (*Confirm the last of a weapon* in Display settings).
847
+
848
+ **The aim guide** draws a ghost of the shell's path while you aim, under this round's wind and
849
+ gravity: by default just the first fifth of the flight, enough to read the lean of the shot without
850
+ giving the landing away. *Aim guide* in Display settings chooses short, the whole flight, or off.
851
+ **Cheat mode** draws the whole flight with a ring where it lands.
852
+
853
+ **Items** are buttons: click a pill to use a battery, raise a shield or arm a trigger, or press
854
+ **B**, **S**, **T**, **H**; **A D** drive if you have fuel. **Space** or the fire button sends the
855
+ shot; **P** pauses; **N** starts the next round.
856
+
857
+ **Restart** on the panel (or **F2**) starts a fresh war from round one, whatever the board is doing:
858
+ a war is five rounds, and a player knocked out early should not have to watch the computer finish.
859
+
860
+ The wind is **simulated air**, behind the land. A small fluid simulation is pushed by the round's
861
+ wind and treats the land as its solid floor, so the air rises over the hills, speeds across the
862
+ crests and rolls into eddies behind them, and it follows the craters as they are blown. You see it
863
+ as thin flow lines, the wind-map kind: each one the recent path of something weightless carried by
864
+ that air, fading in and out and thinning away at the edges of the field. The sky behind them sways
865
+ very slightly, as if seen through moving air. When the wind changes, the air turns round by its own
866
+ momentum rather than jumping. A row of **chevrons** at the top of the field points the way it
867
+ blows, as many as it is strong, brightest just after a change. A computer player decides its shot
868
+ as its turn begins, and you can watch its barrel swing onto it while it thinks.
869
+
870
+ **Cheat mode** (a switch on the panel, also under Display settings → Scorched Yard) draws the
871
+ firing solution while you aim: the shell's own path under this round's wind and the game's
872
+ gravity, clipped where it would meet the dirt, with a ring where it lands. It moves as you move
873
+ the barrel or the power, so you can watch the arc bend under the wind. It is the rules' own
874
+ arithmetic, so what it draws is what the shot does.
875
+
876
+ Over the tank whose turn it is stands the **aim gauge**: a protractor with a tick every fifteen
877
+ degrees, a needle along the current angle whose length is the power, and both numbers at its tip.
878
+ It is dimmed on a computer player's turn, so you can read what it is about to do before it fires.
879
+
880
+ The HUD shows whose turn it is, the round, the **wind** (an arrow and its strength: it changes
881
+ every turn, and it bends every shot; you can see it too — streaks of moving air run level across
882
+ the sky behind the land, short and slow in a breeze, long and quick in a gale, in three bands of
883
+ depth so the air has thickness; the pennant on every turret streams downwind; and the Earth sky's
884
+ clouds go with it), your angle, power and weapon with its count, and every
885
+ tank's health. **Fire** on the HUD does what space does. A shell that lands carves a circle out of
886
+ the dirt; the blast itself is drawn the way the fireworks are — a white core, a shockwave, sparks
887
+ that curve and trail, and smoke that rises, spreads, drifts downwind and thins; the dirt above the
888
+ crater falls until it rests, and a tank left in the air falls with
889
+ it and is hurt by the fall. A blast hurts every tank in reach, most at its centre. A tank at zero
890
+ health explodes, and its blast can take a neighbour with it.
891
+
892
+ A round ends when one tank is left (or none); the survivor scores 200, a kill 100, and a tank
893
+ that kills itself loses 50. **Rounds** (a setting, five out of the box) make a game; the highest
894
+ score at the end wins, and your score goes in the high-score table, this browser's.
895
+
896
+ ### The switches on the panel
897
+
898
+ **Sky** goes round the Galaxy, the Earth and none: the same choice as the **Sky** tab's map. The
899
+ game ships under the Earth, and each round takes its own hour of it, dawn to night, unless **A sky
900
+ per round** is off. **Music** is a march in D minor on oscillators. **Sound** is the shot, the blast, a hit, a
901
+ fall, a death. **Talk** is what the tanks say when they fire, are hit, or die. **Fast** flies
902
+ shells at three times the pace. **Watch** is the attract mode: the computer takes your chair as
903
+ well, so every seat is a computer player and the war runs on by itself — a round rolls into the
904
+ next without waiting at the shop, and when one of them wins the war a fresh one begins. Nothing
905
+ is scored in the high-score table for a war nobody played. Turn it off and the next game seats
906
+ you again; it is also in **Display settings → Scorched Yard → Attract mode**, and it is what to
907
+ leave running on a screen in the corner.
908
+ The rules the original exposed are in **Display settings → Scorched Yard**: the number of
909
+ computer players, the rounds, the **walls** (none loses a shell off the edge, the original's
910
+ default; concrete explodes it there; padded stops it and drops it; rubber bounces it; spring
911
+ bounces it back harder; wraparound brings it in the other side), the **wind** (once a round, which is how it ships: the round's
912
+ direction is drawn when the round starts and held to the end of it, while the strength is drawn
913
+ again for every turn, so the air blows one way and how hard is still something to read before each
914
+ shot; every turn, which is the original's and can turn right around between two shots; every shot;
915
+ or none), **gravity**, the **landscape** the rounds are drawn from (hills, mountains, a valley,
916
+ flat), the **starting cash** and the **interest** rate. They take effect at the next new game.
917
+
720
918
  ## Wolfenstein 3D
721
919
 
722
920
  The shareware episode of Wolfenstein 3D, **Escape from Wolfenstein**, under **Diversions**, first of
@@ -1159,23 +1357,63 @@ these are the settings that buy it back, roughly most expensive first:
1159
1357
  | **Metallic finish** | *Chrome* mirrors a horizon in every face, and the reflection slides as the blocks move; *satin* is the softer highlight along the lit edge. Needs Metallic sheen on. |
1160
1358
  | **Departures and arrivals** | How blocks leave and rejoin the board on a refresh. |
1161
1359
  | **Depth** | How much height foreshortens, 0 to 0.001. 0 is the flat parallel camera the board shipped with: a cube is the same size however high it flies. Raise it and a cube's top grows a little wider than its base and a flying block swells slightly as it rises. |
1162
- | **Star field** | On by default. The stars twinkle, so the board keeps repainting while they are on; switch it off to save that. What the stars *look* like is the **Sky** tab. |
1360
+ | **Sky** | Which sky stands behind the board: the **Galaxy**, the **Earth**, or none. What each sky is made of is the **Sky** tab; this is only the choice, and the Kiosk's left panel follows it. The Galaxy twinkles, so the board keeps repainting while it is up; none saves that. |
1163
1361
  | **Board curve** | How far the board bows toward you. 0 is flat. |
1164
1362
  | **Light** | Where the lamp hangs: *straight above* (the default) lights the whole board evenly, which keeps the front rows as bright as the middle; a corner shades the far slope of the curve and the sides turned away from it. |
1165
1363
 
1166
1364
  ### Sky
1167
1365
 
1168
- One sky, shared by every board that shows stars so the density you choose applies to Block
1169
- space, Markets and Tetrust alike. Whether a given board shows it stays that board's own switch.
1366
+ There are two skies, and every board that has a sky behind it chooses one. The tab opens with the
1367
+ map, which is the whole answer to "which settings apply to which panel":
1368
+
1369
+ | board | draws |
1370
+ |---|---|
1371
+ | **Block space** (and the Kiosk's left panel) | Galaxy |
1372
+ | **Markets & Price** (and the Kiosk's right panel) | Galaxy |
1373
+ | **Tetrust**, **Blockout**, **Blockanoid** | Galaxy |
1374
+ | **Scorched Yard** | Earth |
1375
+
1376
+ Those are the shipped choices; each row is a select with **Galaxy**, **Earth** and **None**, and
1377
+ changing it here is the same setting as the **Sky** row at the top of that board's own tab. The
1378
+ games also carry a **sky** button on their panels that goes round the three. Below the map, the
1379
+ two skies themselves, each with only its own controls; neither is ever dimmed, because any board
1380
+ may be using it.
1381
+
1382
+ **The Galaxy** is the star field, BlockYard's own sky: one set of stars shared by every board that
1383
+ draws it, so the density you choose applies to Block space, the candles and the games alike.
1170
1384
 
1171
1385
  | setting | what it does |
1172
1386
  |---|---|
1387
+ | **Spiral arms** | Lays the stars on slowly turning spiral arms instead of scattering them evenly. One turn takes about a quarter of an hour. |
1388
+ | **Arms centre** | Behind the board, or any of the four corners. A corner crowds the bright nucleus there and sweeps the arms across the panel. |
1173
1389
  | **Star density** / **Star brightness** | How many stars (up to 8x the shipped number) and how strongly they burn. |
1174
- | **Spiral galaxy** | Lays the same stars on slowly turning spiral arms instead of scattering them evenly. One turn takes about a quarter of an hour. |
1175
- | **Galaxy centre** | Behind the board, or any of the four corners. A corner crowds the bright nucleus there and sweeps the arms across the panel. |
1176
- | **Nebulae**, **Dust lanes**, **Star clusters**, **Distant galaxies** | The layers of the sky, each its own switch: gas clouds along the arms, dark ribbons on their inner edges, tight knots out in the halo, and small faint galaxies in the deep field behind everything. |
1177
1390
  | **Star colours** | Warm old stars in the nucleus, blue-white young ones in the arms. Off is one colour of starlight. |
1178
1391
  | **Star glints** | The halo and cross glint on the brightest stars. |
1392
+ | **Nebulae**, **Dust lanes**, **Star clusters**, **Distant galaxies** | The layers of the sky, each its own switch: gas clouds along the arms, dark ribbons on their inner edges, tight knots out in the halo, and small faint galaxies in the deep field behind everything. |
1393
+
1394
+ **The Earth** is a real day drawn from this machine's clock: the sun climbs and sets, the dome
1395
+ goes through night, dawn, day and dusk by the sun's height, clouds drift lit from the sun's side,
1396
+ the moon rises at tonight's phase, and the stars come out as the sun goes down (the Galaxy's
1397
+ density and brightness apply to them; its arms and layers do not, since a spiral over a real night
1398
+ is wrong).
1399
+
1400
+ - **Clock** — real time, a whole day every 24 minutes (to watch it turn), or a fixed hour to
1401
+ admire; **Fixed hour** is that hour.
1402
+ - **Weather** — clear, scattered cloud, overcast, or a storm with rain and lightning; **Cloud
1403
+ cover** overrides the weather's amount, and -1 leaves it to the weather.
1404
+ - **Latitude** — with one set, sunrise, sunset and the sun's height follow the real formula for
1405
+ the date (short winter days, the midnight sun); left unset, the day runs six to six.
1406
+ - **Sun rays**, **Rainbow** (opposite a low sun, in scattered weather) and **Shooting stars** (now
1407
+ and then, at night) are the touches, each a switch.
1408
+ - **Moon** — up every night, highest at midnight and never thinner than a fat crescent (the
1409
+ shipped choice), or on its real track at its real phase, which some nights means no moon at all.
1410
+
1411
+ The sun and the moon are drawn in front of the clouds, so neither is lost behind one, and a board
1412
+ whose land fills the lower part of the panel sets its horizon where the land is, so they go down
1413
+ behind the hills rather than under them.
1414
+
1415
+ A board under the Earth plays no idle effects: a supernova over a blue afternoon is wrong, on the
1416
+ block board and on the candles alike. Their switches are kept, and the Galaxy gets them back.
1179
1417
 
1180
1418
  ### Space effects and Market effects
1181
1419
 
@@ -1232,7 +1470,7 @@ What the sky is *made of* — density, brightness, nebulae, dust and the rest
1232
1470
 
1233
1471
  ### Blockanoid
1234
1472
 
1235
- The Arkanoid court. **Star field**, **spiral galaxy**, **neon bricks** and **sound effects** are the
1473
+ The Arkanoid court. **Sky** (the Galaxy, the Earth, or none), **neon bricks** and **sound effects** are the
1236
1474
  same switches that sit on the game's own panel. Two more are here only, and they change the *rules*:
1237
1475
 
1238
1476
  | setting | what it does |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blockyard",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Live web monitor, 3D block-space viewer, block explorer and markets view for a Bitcoin Core node",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "type": "module",
23
23
  "engines": {
24
- "node": ">=22.0.0"
24
+ "node": ">=22.2.0"
25
25
  },
26
26
  "main": "server/main.js",
27
27
  "bin": {
@@ -35,6 +35,8 @@
35
35
  "config/pool-map.json",
36
36
  "systemd/",
37
37
  "docs/*.md",
38
+ "!docs/STATE-*.md",
39
+ "!docs/PRIVATE-*.md",
38
40
  "CHANGELOG.md",
39
41
  "SECURITY.md",
40
42
  "NOTICE"
@@ -1818,6 +1818,7 @@ body { font-variant-numeric: tabular-nums; }
1818
1818
  .cfggroup { border: 1px solid var(--line-soft); border-radius: var(--radius); padding: 8px 10px 10px; margin-bottom: 10px; background: var(--bg-2); }
1819
1819
  .cfggroup > h3 { margin: 0 0 4px; font-size: 11px; letter-spacing: .09em; text-transform: uppercase; color: var(--accent); }
1820
1820
  .cfggroup > p { margin: 0 0 8px; color: var(--fg-faint); font-size: 11px; line-height: 1.4; }
1821
+ .cfggroup > p.cfgheld { color: var(--warn); }
1821
1822
  /* packed (2026-09-15): rows 5px not 7, hints tighter, and on the effects tabs the switches two across
1822
1823
  with the hint as the row's tooltip */
1823
1824
  .cfgrow { display: grid; grid-template-columns: 1fr auto; gap: 3px 10px; align-items: center; padding: 5px 0; border-top: 1px solid var(--line-soft); }
@@ -1938,6 +1939,92 @@ body { font-variant-numeric: tabular-nums; }
1938
1939
  /* Blockanoid's court is 13 x 26: narrower than Blockout's and taller, which is what gives Vaus a
1939
1940
  court you can dig a channel up the side of. Same furniture, its own shape. */
1940
1941
  .tetcard .treemapwrap.tetwell.banwell { aspect-ratio: 14 / 27; }
1942
+ /* SCORCHED YARD's field is 96 x 48, wide and low like the original's 640 x 350 screen: it takes
1943
+ the card's width and its height follows (docs/PLAN-SCORCHED-YARD.md). The page gives the field the
1944
+ first column, as the DOS games do, so the HUD sits beside a field that fills the window's width. */
1945
+ #scorched { grid-template-columns: minmax(0, 1fr) 270px; justify-content: stretch; }
1946
+ #scorched > .tetcard { min-width: 0; }
1947
+ .tetcard .treemapwrap.tetwell.seyard { width: min(100%, calc((100vh - 170px) * 2)); height: auto; aspect-ratio: 2 / 1; }
1948
+ /* two canvases stacked: the land under, the actors over, the sky behind both (scorchedyard.js);
1949
+ scaled a little from the bottom centre so the board's own margins leave the frame -- the land
1950
+ meets the bottom edge and runs off both sides, and no sky shows under it (operator, 2026-09-16:
1951
+ "Play field needs to go to the edges") */
1952
+ .seyard { overflow: hidden; }
1953
+ .seyard canvas { position: absolute; inset: 0; transform: scale(1.085); transform-origin: 50% 100%; }
1954
+ /* THE WIND'S OWN PLANE: flat, behind the land, and NOT scaled with the board -- it is not in the
1955
+ scene, it is the air behind it (operator: "drawn behind everything else on a flat plane") */
1956
+ .seyard canvas.sywind { width: 100%; height: 100%; transform: none; }
1957
+ /* the two numbers a player actually tunes: click one and type it */
1958
+ #syStats b.syval { cursor: text; border-bottom: 1px dotted var(--line); }
1959
+ #syStats b.syval:hover { color: var(--accent); }
1960
+ .syrestart { width: 100%; margin-top: 6px; }
1961
+ /* the controls (docs/PLAN-SCORCHED-YARD.md §12): the confirm state, item buttons, the weapon grid, the keys table */
1962
+ .syfire.syconfirm { background: var(--bad); border-color: var(--bad); }
1963
+ button.sypillbtn { cursor: pointer; font: inherit; }
1964
+ button.sypillbtn:hover { border-color: var(--accent); }
1965
+ .syweapons { position: absolute; right: 12px; top: 12px; z-index: 4; width: min(360px, 60%); max-height: 70%; overflow: auto; background: rgba(8, 10, 14, .9); border: 1px solid var(--line); border-radius: 6px; padding: 8px; }
1966
+ .syweapons.hidden { display: none; }
1967
+ .syweaponshead { display: flex; align-items: baseline; gap: 6px; font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--fg-dim); margin-bottom: 6px; }
1968
+ .syweaponshead small { text-transform: none; letter-spacing: 0; }
1969
+ .syweaponlist { display: grid; gap: 3px; }
1970
+ .syweapon { display: grid; grid-template-columns: 1fr auto auto 1.6em; gap: 8px; align-items: baseline; text-align: left; padding: 4px 6px; background: transparent; border: 1px solid transparent; border-radius: 4px; color: var(--text); font: inherit; font-size: 12px; cursor: pointer; }
1971
+ .syweapon:hover { border-color: var(--line); }
1972
+ .syweapon.on { border-color: var(--accent); }
1973
+ .syweapon i { color: var(--fg-dim); font-style: normal; font-size: 11px; }
1974
+ .syweapon kbd { color: var(--accent); font-family: var(--mono); text-align: center; }
1975
+ table.sykeys { border-collapse: collapse; width: 100%; }
1976
+ table.sykeys th { text-align: left; vertical-align: top; padding: 2px 8px 2px 0; font-weight: 500; color: var(--fg-dim); white-space: nowrap; }
1977
+ table.sykeys td { padding: 2px 0; }
1978
+ /* the Sky tab's map of boards to skies, and the two headings under it (docs/PLAN-SKIES.md) */
1979
+ .skymap { border-collapse: collapse; }
1980
+ .skymap th { text-align: left; font-weight: 500; padding: 3px 14px 3px 0; white-space: nowrap; }
1981
+ .skymap th small { display: block; color: var(--fg-faint); font-size: 11px; font-weight: 400; }
1982
+ .skymap td { padding: 3px 0; }
1983
+ .cfghead { margin: 18px 0 6px; padding-top: 10px; border-top: 1px solid var(--line); }
1984
+ .cfghead b { display: block; font-size: 13px; letter-spacing: .06em; text-transform: uppercase; color: var(--accent); }
1985
+ .cfghead i { display: block; color: var(--fg-dim); font-style: normal; font-size: 12px; margin-top: 2px; }
1986
+ .syedit { width: 5.2em; font: inherit; color: var(--text); background: var(--bg); border: 1px solid var(--accent); border-radius: 3px; padding: 0 2px; text-align: right; }
1987
+ .syfire { width: 100%; margin: 2px 0 10px; }
1988
+ .syfire:disabled { opacity: .45; cursor: default; }
1989
+ .sytanks { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; font-size: 12px; }
1990
+ .sytank { display: grid; grid-template-columns: 10px auto 1fr auto; gap: 3px 7px; align-items: center; padding: 3px 6px; border-radius: var(--radius-sm); border: 1px solid transparent; }
1991
+ .sytank.now { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
1992
+ .sytank.dead { opacity: .45; }
1993
+ .sytank b { font-weight: 600; }
1994
+ .sytank meter { width: 100%; height: 9px; }
1995
+ .sytank span { font-family: var(--mono); font-variant-numeric: tabular-nums; min-width: 3ch; text-align: right; }
1996
+ .sytank small { grid-column: 2 / -1; color: var(--fg-faint); font-size: 10.5px; }
1997
+ .sydot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; background: var(--fg-faint); }
1998
+ /* what a tank says: a bubble over the field at the tank's place (its position is two custom
1999
+ properties set through the CSSOM, never a style attribute -- the CSP) */
2000
+ .sytalk { position: absolute; z-index: 3; left: var(--x, 50%); top: var(--y, 50%); transform: translate(-50%, -100%);
2001
+ padding: 4px 9px; border-radius: 10px; background: rgba(14, 16, 22, .88); border: 1px solid var(--line); color: var(--fg);
2002
+ font-size: 12px; white-space: nowrap; pointer-events: none; }
2003
+ .sytalk.hidden { display: none; }
2004
+ /* each dot takes its tank's own colour, set through the CSSOM by scorchedyard.js drawTanks (the
2005
+ colour follows the personality, and a class per seat painted the seat's old colour instead) */
2006
+ /* the items line: pills, lit when armed */
2007
+ .syitems { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 10px; font-size: 11px; min-height: 18px; }
2008
+ .sypill { padding: 2px 7px; border-radius: 999px; border: 1px solid var(--line); color: var(--fg-dim); }
2009
+ .sypill.on { border-color: var(--accent); color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
2010
+ /* THE SHOP (M2): in the overlay between rounds, two columns of rows -- name, note, price and pack,
2011
+ what you own, a buy button while you can afford it. Scrolls inside the card on a short window. */
2012
+ .syshop { width: min(100%, 980px); max-height: min(66vh, 620px); overflow: auto; text-align: left; background: rgba(8, 10, 14, .92);
2013
+ border: 1px solid var(--line); border-radius: var(--radius); padding: 8px 12px 10px; font-size: 11.5px; }
2014
+ .syshop.hidden { display: none; }
2015
+ .syshophead { display: flex; align-items: center; gap: 8px; font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: var(--fg-dim); margin-bottom: 4px; }
2016
+ .syshophead b { color: var(--ok); font-family: var(--mono); letter-spacing: 0; text-transform: none; font-size: 13px; }
2017
+ .sycols { display: grid; grid-template-columns: 1fr 1fr; gap: 0 18px; }
2018
+ .sycols h4 { margin: 6px 0 2px; font-size: 10.5px; letter-spacing: .09em; text-transform: uppercase; color: var(--accent); }
2019
+ .syrow { display: grid; grid-template-columns: minmax(0, 1fr) auto auto auto; gap: 1px 8px; align-items: center; padding: 3px 0; border-top: 1px solid var(--line-soft); }
2020
+ .syrow b { font-weight: 600; }
2021
+ .syrow .syhint { grid-column: 1 / -1; grid-row: 2; color: var(--fg-faint); font-size: 10px; }
2022
+ .syrow .syprice { font-family: var(--mono); color: var(--fg-dim); font-variant-numeric: tabular-nums; white-space: nowrap; }
2023
+ .syrow .syown { color: var(--ok); font-size: 10.5px; min-width: 5ch; text-align: right; }
2024
+ .syrow.poor .syprice { color: var(--fg-faint); }
2025
+ .sybuy { font-size: 10.5px; padding: 2px 8px; }
2026
+ .sybuy:disabled { opacity: .35; cursor: default; }
2027
+ @media (max-width: 900px) { .sycols { grid-template-columns: 1fr; } }
1941
2028
  .tetwell canvas { width: 100%; height: 100%; display: block; background: transparent; border: 0; } /* the sky shows through */
1942
2029
  /* THE PANEL IS THE SKY (operator: "Have this entire panel filled black and rendering the spiral
1943
2030
  galaxy for this display. Have the text floating over the spiral galaxy, and then the playboard