blockyard 0.0.9 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +251 -1
- package/README.md +42 -23
- package/bin/blockyard.js +2 -1
- package/docs/API.md +16 -14
- package/docs/ARCHITECTURE.md +92 -5
- package/docs/CONFIGURATION.md +33 -26
- package/docs/GETTING-STARTED.md +5 -2
- package/docs/INSTALL.md +90 -33
- package/docs/MEASUREMENTS.md +147 -0
- package/docs/SECURITY.md +32 -15
- package/docs/TROUBLESHOOTING.md +35 -1
- package/docs/USER-GUIDE.md +266 -26
- package/package.json +1 -1
- package/public/404.html +1 -1
- package/public/css/app.css +306 -82
- package/public/donate-qr.png +0 -0
- package/public/index.html +295 -103
- package/public/js/agents.js +228 -51
- package/public/js/app.js +82 -8
- package/public/js/blockscene3d.js +179 -27
- package/public/js/charts.js +21 -21
- package/public/js/depthchart.js +31 -27
- package/public/js/details3d.js +1456 -71
- package/public/js/doom.js +31 -0
- package/public/js/dosaudio.js +48 -0
- package/public/js/dosgame.js +389 -0
- package/public/js/dosio.js +186 -0
- package/public/js/dospc.js +1353 -0
- package/public/js/dosworker.js +196 -0
- package/public/js/login.js +5 -0
- package/public/js/markets.js +46 -8
- package/public/js/mining.js +310 -32
- package/public/js/panels.js +14 -10
- package/public/js/pricechart.js +14 -13
- package/public/js/quake.js +20 -0
- package/public/js/settings.js +103 -21
- package/public/js/soundcard.js +459 -0
- package/public/js/theme.js +235 -0
- package/public/js/wolf3d.js +22 -0
- package/public/js/x86.js +1978 -0
- package/scripts/donate-qr.py +12 -9
- package/scripts/dos-bench.js +56 -0
- package/scripts/setup.js +34 -12
- package/scripts/shots.mjs +6 -0
- package/scripts/smoke.sh +1 -1
- package/scripts/tls.js +31 -0
- package/server/chain/index/build.js +21 -4
- package/server/collect/monitor.js +30 -1
- package/server/collect/network.js +295 -0
- package/server/config.js +46 -22
- package/server/http/api.js +49 -5
- package/server/http/games.js +77 -0
- package/server/http/server.js +8 -0
- package/server/main.js +53 -8
- package/server/tls/selfsigned.js +160 -0
- package/systemd/blockyard.service +7 -5
- package/docs/PRIVATE-LEADERBOARD.md +0 -230
- package/docs/STATE-2026-09-09.md +0 -200
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -192,8 +192,9 @@ it directly; running the file as a script calls it and prints the banner.
|
|
|
192
192
|
missing is skipped with a logged reason rather than kept as a permanently
|
|
193
193
|
offline panel. `wireMonitor()` connects each monitor's events to the SSE hub
|
|
194
194
|
and coalesces pushes to one snapshot per second.
|
|
195
|
-
8. `MarketFeed` is created if `markets.enabled` is set. It stays idle until the
|
|
196
|
-
|
|
195
|
+
8. `MarketFeed` is created if `markets.enabled` is set. It stays idle until the Markets
|
|
196
|
+
API is requested with the **Enable market polling** setting on (off by default; read from
|
|
197
|
+
the shared settings file per request, `marketsPollingOn` in `http/api.js`).
|
|
197
198
|
9. One HTTP(S) server is created per bound address, all sharing the same `app`.
|
|
198
199
|
An address missing at boot is skipped with a warning. Boot is fatal only when
|
|
199
200
|
none of the configured addresses exist.
|
|
@@ -346,7 +347,9 @@ Bitstamp, Bitfinex and OKX.
|
|
|
346
347
|
with its error, never zero-filled. Tickers older than three intervals are marked
|
|
347
348
|
`stale`.
|
|
348
349
|
|
|
349
|
-
|
|
350
|
+
Polling is off by default — the **Enable market polling** Display setting turns it on, and a
|
|
351
|
+
request with it off parks the feed. `BLOCKYARD_MARKETS=0` (or `markets.enabled: false`)
|
|
352
|
+
removes the feed altogether.
|
|
350
353
|
|
|
351
354
|
### 2.4 The RPC lane (`server/rpc/client.js`)
|
|
352
355
|
|
|
@@ -523,11 +526,12 @@ flowchart LR
|
|
|
523
526
|
|
|
524
527
|
### 2.7 Auth (`server/auth/*`)
|
|
525
528
|
|
|
526
|
-
- **
|
|
529
|
+
- **Sign-in by default** (since 2026-09-15; it shipped open in 0.0.9), bound to
|
|
530
|
+
`127.0.0.1`. With `auth.enabled: false` (`BLOCKYARD_AUTH=0`), every request is
|
|
527
531
|
served as a frozen anonymous user with role `viewer`. That ceiling is hardcoded,
|
|
528
532
|
not configurable. Admin routes return 403 in both modes, CSRF is not needed
|
|
529
533
|
because there is no cookie to ride, and rate limits apply per client address.
|
|
530
|
-
- **Accounts
|
|
534
|
+
- **Accounts** (the default; `BLOCKYARD_AUTH=1` restores them after an override):
|
|
531
535
|
- scrypt password hashes (upgraded to current parameters on login)
|
|
532
536
|
- session tokens that are stored hashed, with idle and absolute TTLs
|
|
533
537
|
- double-submit CSRF (the token in a header or body is compared with the
|
|
@@ -659,6 +663,7 @@ loaded from the same origin. There is no build step and no framework.
|
|
|
659
663
|
| `settings.js` | display settings: `DEFAULTS`, the `PANEL` rows of the settings dialog, `normalise()`, and the option builders (`spaceOptions`, `enabledEffects`, ...) the boards read; stored on the server (`/api/settings`) with a `localStorage` copy |
|
|
660
664
|
| `about.js` | the About page (version, system and node info) |
|
|
661
665
|
| `tetris.js` / `tetrust.js`, `breakout.js` / `blockout.js`, `arkanoid.js` / `blockanoid.js`, `tetsound.js` | the Diversions: pure game rules in the first file of each pair, the tab drawn on the 3D engine in the second, and Tetrust's sound |
|
|
666
|
+
| `x86.js`, `dospc.js`, `soundcard.js`, `dosworker.js`, `dosaudio.js`, `dosio.js`, `dosgame.js`, `doom.js`, `quake.js` | the DOS Diversions: an i386 interpreter, the PC around it (DOS/4GW for DOOM, the go32 stub and CWSDPMI for Quake), a Sound Blaster Pro 2 with an OPL3, the worker the machine runs in, the AudioWorklet it plays through, the pure keyboard/config/text-mode helpers, the shared tab, and each game's own few lines (section 3.4) |
|
|
662
667
|
| `fmt.js` | formatters: decimal units (as the node prints them), `–` for anything absent |
|
|
663
668
|
| `login.js` | the login page (a separate file because of the CSP) |
|
|
664
669
|
|
|
@@ -790,6 +795,88 @@ What this means for front-end code:
|
|
|
790
795
|
|
|
791
796
|
---
|
|
792
797
|
|
|
798
|
+
### 3.4 The DOS Diversions: Wolfenstein 3D, DOOM and Quake
|
|
799
|
+
|
|
800
|
+
The shareware `WOLF3D.EXE` v1.4, `DOOM.EXE` v1.9 and `QUAKE.EXE` v1.06 run unmodified on a PC emulated in
|
|
801
|
+
the browser.
|
|
802
|
+
Nothing is ported and no dependency is used; every layer is this repository's own:
|
|
803
|
+
|
|
804
|
+
```
|
|
805
|
+
wolf3d.js / doom.js / quake.js -> dosgame.js (page) --scancodes, mouse, run/pause--> dosworker.js (Worker)
|
|
806
|
+
^ |
|
|
807
|
+
| frames (320x200 indices + palette), text-mode cells, stats | createPC() dospc.js
|
|
808
|
+
+----------------------------------------------------------------------+ createCpu() x86.js
|
|
809
|
+
dosaudio.js (AudioWorklet) <--stereo PCM over a MessagePort-----------------+ soundcard.js (SB Pro 2 + OPL3)
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
- **`x86.js`** is a user-mode i386 with an x87, interpreted. No paging or rings: a DOS extender's
|
|
813
|
+
program runs in protected mode, and segment registers carry a base and a size. EIP is
|
|
814
|
+
kept linear and converted at the edges (a pushed return address, a loaded jump target) against
|
|
815
|
+
the code segment's base, ESP is an offset in SS: DOS/4GW's segments are all at 0, DJGPP's at the
|
|
816
|
+
program's memory block. A code segment whose descriptor is 16-bit decodes 16-bit (DJGPP's
|
|
817
|
+
start-up and exit run small 16-bit helpers in DOS memory). **Real mode** is a switch
|
|
818
|
+
(`cpu.realMode`) for Wolfenstein 3D: a segment's base is its value times sixteen, code is 16-bit,
|
|
819
|
+
the stack pointer is SP and wraps inside its segment, the string instructions count with SI, DI
|
|
820
|
+
and CX, and an interrupt pushes a 16-bit frame and goes through the vector table at 0:0. Real-mode
|
|
821
|
+
code runs through `step()` rather than the decoded cache: it needs about a third of the speed it
|
|
822
|
+
gets. The page's CSP forbids eval, so there
|
|
823
|
+
is no JIT; speed comes from keeping every value an int32 (a `>>> 0` above 2^31 is a double and,
|
|
824
|
+
in a closure variable, an allocation), lazy flags recorded in an `Int32Array`, one try/catch
|
|
825
|
+
around the loop rather than each instruction, and 32-bit fast paths for the instructions
|
|
826
|
+
compilers emit most. The FPU keeps its stack in a Float64Array and converts operands through
|
|
827
|
+
typed-array views. **Decoded instructions are cached**: each is decoded once into three int32s
|
|
828
|
+
(a handler number with its registers packed beside it, a displacement, an immediate) in an
|
|
829
|
+
Int32Array per 4 KB page, and `run()` dispatches on the handler number with one switch; the forms
|
|
830
|
+
it does not specialise run through `step()`. Not closures -- a closure per instruction made every
|
|
831
|
+
call megamorphic and ran slower than the interpreter. Writes into a page holding decoded code
|
|
832
|
+
clear only the instructions they overlap (DOOM and Quake both patch constants into their own span
|
|
833
|
+
drawers on every call), and the machine calls `cpu.invalidate()` after it writes memory directly.
|
|
834
|
+
The ALU operations have a handler each (their flag bookkeeping inline, not one shared routine
|
|
835
|
+
branching on the operation), so do the x87 forms Quake runs most, and the loop forms a memory
|
|
836
|
+
operand's address inline before the switch: the switch is too big for V8 to inline helpers into
|
|
837
|
+
every case, and those calls were most of what was left. About 141 million instructions a second on
|
|
838
|
+
Quake headless on one core and 115-118 in a Chromium worker; Quake's timedemo 52.5 fps of wall time
|
|
839
|
+
(MEASUREMENTS §32-35).
|
|
840
|
+
- **`dospc.js`** is the machine, and it plays whichever DOS extender the program was bound to.
|
|
841
|
+
`boot()` tells them apart by the file: an EXE with neither an LE nor a COFF image inside is a
|
|
842
|
+
plain DOS program, and `bootMZ` loads it as DOS did -- a PSP, the image after it with its segment
|
|
843
|
+
relocations applied, the rest of conventional memory its block, the vector table filled with
|
|
844
|
+
pointers at stubs the machine answers (a program that installs its own handler gets it called),
|
|
845
|
+
and DOS's allocator behind INT 21h 48h/49h/4Ah, which Wolfenstein 3D uses to shrink itself and
|
|
846
|
+
claim the rest. `loadLE` finds DOOM's LE executable inside the DOS/4GW
|
|
847
|
+
stub, loads it at +1 MB and applies its fixups; `parseCoff`/`bootCoff` find Quake's COFF image
|
|
848
|
+
behind the go32 stub and do what that stub leaves behind -- a memory block with the sections in
|
|
849
|
+
it, selectors based at it, a transfer buffer with the PSP right below it (DJGPP's libc finds the
|
|
850
|
+
PSP by subtracting 100h), and the "stubinfo" crt0 reads through FS. The extenders themselves never
|
|
851
|
+
run; this file answers INT 21h (files from an in-memory, case-insensitive directory tree, written
|
|
852
|
+
files handed to the host on close, and a real system file table, because DJGPP's `fstat` walks
|
|
853
|
+
it), INT 31h DPMI (descriptors with base and size, memory blocks, protected-mode vectors,
|
|
854
|
+
simulated real-mode interrupts), INT 10h/16h/33h, and the hardware a DOS game programs directly:
|
|
855
|
+
the 8259s, the 8254 (with the BIOS tick count kept in step with it, which DJGPP's `uclock` reads),
|
|
856
|
+
the keyboard controller, and a VGA with planar memory, unchained mode, write mode 1's latched copy
|
|
857
|
+
(Wolfenstein 3D and DOOM copy between pages with it) and CRTC page flipping (how DOOM and
|
|
858
|
+
Wolfenstein 3D draw) as well as the linear window (how Quake does). The clock is injected (`now()`): wall
|
|
859
|
+
time in the worker, instruction count in tests, so a headless boot is the same run every time.
|
|
860
|
+
- **`soundcard.js`** is a Sound Blaster Pro 2 at 220h/IRQ 7/DMA 1 — the DSP's command set and the
|
|
861
|
+
8237 DMA controller it pulls samples through — and an OPL3 modelled as operators with
|
|
862
|
+
documented envelope rates. `tick(t)` produces output for the machine time that passed and raises
|
|
863
|
+
the end-of-block interrupt from inside the same loop.
|
|
864
|
+
- **`dosworker.js`** takes a game's name, fetches its files (`dosio.js` `GAMES`), and runs the
|
|
865
|
+
machine in ~10 ms slices, yielding between them so input arrives. It sends a frame when the CRTC
|
|
866
|
+
start address or the palette changed, or once the linear window has been written and a slice has
|
|
867
|
+
passed without more writes (never half of Quake's copy). The frame buffer bounces between the
|
|
868
|
+
worker and the page so no frame allocates. Savegames and configs are kept in IndexedDB, one
|
|
869
|
+
database a game.
|
|
870
|
+
- **`dosgame.js`** is a game's tab: it draws, captures input, pauses when the tab is not on screen
|
|
871
|
+
(the worker's clock stops, so nothing moves), and uses a ScriptProcessor when `audioWorklet` is
|
|
872
|
+
unavailable — a plain-HTTP LAN address is not a secure context. `doom.js` and `quake.js` give it
|
|
873
|
+
names, key lists and switches, and so does `wolf3d.js`.
|
|
874
|
+
|
|
875
|
+
The game files are served from `games/<game>_dos/` by `server/http/games.js`
|
|
876
|
+
(`/games/<game>/<path>`: a game it names, at most one directory and 8.3 names of `.EXE`, `.WAD`,
|
|
877
|
+
`.PAK`, `.CFG` and `.WL1`, behind the session when accounts are on) rather than from `public/`, whose every
|
|
878
|
+
file feeds the build id.
|
|
879
|
+
|
|
793
880
|
## 4. The 3D engine
|
|
794
881
|
|
|
795
882
|
The 3D viewer turns a set of transactions, or any caller-supplied tiles, into
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -95,18 +95,20 @@ Durations are in milliseconds unless the name says otherwise (`retentionHours`).
|
|
|
95
95
|
|
|
96
96
|
| key | default | meaning |
|
|
97
97
|
|---|---|---|
|
|
98
|
-
| `server.host` | `"
|
|
98
|
+
| `server.host` | `"127.0.0.1"` | Address to listen on — this machine only, by default. Kept for compatibility; `server.hosts` wins when both are set. |
|
|
99
99
|
| `server.hosts` | *(unset; falls back to `host`)* | Addresses to listen on: an array (`["192.0.2.10", "2001:db8::10"]`), or a single string with commas (`"192.0.2.10,198.51.100.7"`). One HTTP server is started per address, and all of them share sessions, rate limits and monitors. Entries must be address literals: IPv4, IPv6, `0.0.0.0`, `::`, or `localhost`. Hostnames are refused. See [Binding](#binding-to-specific-addresses). |
|
|
100
100
|
| `server.port` | `21000` | TCP port. It is the same port on every address. It must be an integer from 1 to 65535. |
|
|
101
101
|
| `server.allowCidrs` | `[]` | Client allowlist. Empty means every client that can reach the port is admitted. Otherwise only addresses inside one of the networks connect, and everyone else gets HTTP 403 (the reason goes to the server log). Entries are CIDRs or bare addresses (a bare address means `/32` or `/128`), IPv4 or IPv6, compared bit by bit. An entry that cannot be parsed stops the boot. |
|
|
102
102
|
| `server.trustProxy` | `false` | When `true`, the client address is the **first** entry of the `X-Forwarded-For` header instead of the socket's peer address. That address feeds the CIDR allowlist, the rate limits and the audit log. Turn it on only when a reverse proxy you control is the sole way in and it sets that header. Otherwise any client can pick its own address and walk past `allowCidrs`. |
|
|
103
|
-
| `server.tls.
|
|
103
|
+
| `server.tls.enabled` | `true` | HTTPS on every listener. With no `cert`/`key` named, the server makes its own self-signed certificate under `<data>/tls/` on first start (see INSTALL §9). `false` serves plain HTTP, for a reverse proxy that terminates TLS. |
|
|
104
|
+
| `server.tls.cert` | `null` | Path to a PEM certificate of your own, used instead of the made one. Set it together with `key`. |
|
|
104
105
|
| `server.tls.key` | `null` | Path to the PEM private key for `cert`. This file is secret. |
|
|
105
106
|
| `server.tls.hstsMs` | `172800000` (2 days) | `max-age` of the `Strict-Transport-Security` header. It is sent on TLS responses only. `0` turns it off. `includeSubDomains` and `preload` are never sent. |
|
|
106
107
|
|
|
107
108
|
When TLS is on, `auth.secureCookie` is forced to `true`. At startup the server logs
|
|
108
109
|
the certificate's SHA-256 fingerprint and whether it is self-signed. It also warns
|
|
109
|
-
when the certificate expires within 14 days
|
|
110
|
+
when the certificate expires within 14 days — and remakes its own, if it made it, at that
|
|
111
|
+
point or when the certificate no longer names a bound address.
|
|
110
112
|
|
|
111
113
|
### nodes
|
|
112
114
|
|
|
@@ -340,17 +342,18 @@ Available actions:
|
|
|
340
342
|
The Markets tab fetches public BTC/USD prices, hourly candles and order books from
|
|
341
343
|
five exchanges over HTTPS: Coinbase, Kraken, Bitstamp, Bitfinex and OKX (OKX quotes
|
|
342
344
|
BTC/USDT). The fetches run on the server, not in the browser. This is BlockYard's
|
|
343
|
-
only outbound connection other than the node
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
345
|
+
only outbound connection other than the node, and **polling is off by default**: a fresh
|
|
346
|
+
install makes no request to anyone but the node until someone ticks **Display settings → Markets & Price → Enable market polling** in the browser (a Display
|
|
347
|
+
setting, stored in `config/blockyard.json` and shared by every screen; no restart).
|
|
348
|
+
Exchanges then see this server's IP address and a User-Agent, nothing about the node. Polling starts when a browser asks for market data
|
|
349
|
+
-- the Markets and Kiosk tabs, and Overview's price line (Display settings → Markets &
|
|
350
|
+
Price → Price line on Overview) -- and stops `idleAfterMs` after the last request, so an
|
|
351
|
+
unwatched monitor makes no exchange traffic. With that line switched off, only Markets
|
|
352
|
+
and Kiosk start it. The exchange list is fixed in code.
|
|
350
353
|
|
|
351
354
|
| key | default | meaning |
|
|
352
355
|
|---|---|---|
|
|
353
|
-
| `markets.enabled` | `true` | `false` removes the feed entirely
|
|
356
|
+
| `markets.enabled` | `true` | `false` removes the feed entirely: no request is ever made, Markets and Kiosk say so, and the **Enable market polling** checkbox cannot turn it on. |
|
|
354
357
|
| `markets.tickerMs` | `15000` | Ticker (last, bid, ask, 24 h volume) refresh. |
|
|
355
358
|
| `markets.candleMs` | `300000` | Hourly candle refresh. |
|
|
356
359
|
| `markets.bookMs` | `30000` | Order book refresh, for the depth chart. |
|
|
@@ -378,12 +381,13 @@ Environment variables override `config/local.json`.
|
|
|
378
381
|
| variable | sets | type | default | meaning |
|
|
379
382
|
|---|---|---|---|---|
|
|
380
383
|
| `BLOCKYARD_CONFIG` | *(which file is read)* | path or `none` | `<repo>/config/local.json` | Configuration file to read. `none`/`off`/`no`/`-` reads no file. |
|
|
381
|
-
| `BLOCKYARD_BIND` | `server.host` | list | `
|
|
382
|
-
| `BLOCKYARD_HOST` | `server.host` | list | `
|
|
384
|
+
| `BLOCKYARD_BIND` | `server.host` | list | `127.0.0.1` | Listen address(es), for example `0.0.0.0` or `192.0.2.10,2001:db8::10`. If both are set, this wins over `BLOCKYARD_HOST`. Ignored when the file sets `server.hosts` (see [Known quirks](#known-quirks)). |
|
|
385
|
+
| `BLOCKYARD_HOST` | `server.host` | list | `127.0.0.1` | Same as `BLOCKYARD_BIND`. |
|
|
383
386
|
| `BLOCKYARD_PORT` | `server.port` | number | `21000` | Listen port. |
|
|
384
387
|
| `BLOCKYARD_ALLOW_CIDRS` | `server.allowCidrs` | list | *(empty: everyone)* | Client allowlist, for example `192.0.2.0/24,2001:db8::/32`. |
|
|
385
388
|
| `BLOCKYARD_TRUST_PROXY` | `server.trustProxy` | boolean | `false` | Take the client address from `X-Forwarded-For`. |
|
|
386
|
-
| `
|
|
389
|
+
| `BLOCKYARD_TLS` | `server.tls.enabled` | boolean | `true` | `0` serves plain HTTP (behind a TLS-terminating proxy). |
|
|
390
|
+
| `BLOCKYARD_TLS_CERT` | `server.tls.cert` | path | unset | PEM certificate of your own, instead of the made one. Set it together with `BLOCKYARD_TLS_KEY`. |
|
|
387
391
|
| `BLOCKYARD_TLS_KEY` | `server.tls.key` | path | unset | PEM private key. Set it together with `BLOCKYARD_TLS_CERT`. |
|
|
388
392
|
| `BLOCKYARD_NODE_URL` | `nodes[0].rpcUrl` | URL | `http://127.0.0.1:8332` | RPC endpoint of the first node. |
|
|
389
393
|
| `BLOCKYARD_DATADIR` | `nodes[0].datadir` | path | `/home/bitcoin/.bitcoin` | Data directory of the first node. It also **clears** `nodes[0].cookieFile`, so the cookie is looked up under the new datadir. |
|
|
@@ -404,7 +408,7 @@ Environment variables override `config/local.json`.
|
|
|
404
408
|
| `BLOCKYARD_ALLOW_WRITES_WITHOUT_AUTH` | `actions.allowWritesWithoutAuth` | boolean | `false` | Permit actions while accounts are off. |
|
|
405
409
|
| `BLOCKYARD_LOG_SOURCE` | `log.enabled` | boolean | `false` | `1` tails node log files; `0` (or unset) runs on RPC alone. |
|
|
406
410
|
| `BLOCKYARD_LOG_LEVEL` | `log.level` | string | `info` | `debug`, `info`, `warn` or `error`. |
|
|
407
|
-
| `BLOCKYARD_MARKETS` | `markets.enabled` | boolean | `true` | `0`
|
|
411
|
+
| `BLOCKYARD_MARKETS` | `markets.enabled` | boolean | `true` | `0` removes the Markets feed; the polling checkbox then cannot turn it on. |
|
|
408
412
|
| `BLOCKYARD_MINING` | *(none)* | `0` or anything | on | `0` turns off miner attribution, which decodes each block's coinbase to show the pool tag. Block sizes, fees and weights are unaffected. Only the literal `0` disables it. |
|
|
409
413
|
| `BLOCKYARD_MINING_BACKFILL` | *(none)* | number | `36` | How many recent blocks are attributed to miners at startup. |
|
|
410
414
|
| `BLOCKYARD_MINING_TEMPLATE` | *(none)* | `0` or anything | on | `0` turns off the "block being built" card. Since 2026-09-13 the template is **assembled from the mempool this monitor already reads**, so leaving it on costs your node no RPC call at all — it costs this process ~50-70 ms of CPU per assembly. Before that it was a `getblocktemplate` worth over a second of the node's single RPC thread, which is why the switch exists. Only the literal `0` disables it. |
|
|
@@ -577,10 +581,10 @@ instead:
|
|
|
577
581
|
BLOCKYARD_ADMIN_PASSWORD='choose-a-long-passphrase' npm start
|
|
578
582
|
```
|
|
579
583
|
|
|
580
|
-
Then create personal accounts with `npm run user -- create <name> <role>`.
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
an SSH tunnel.
|
|
584
|
+
Then create personal accounts with `npm run user -- create <name> <role>`. HTTPS is on by
|
|
585
|
+
default with the monitor's own certificate; if you turn it off (`BLOCKYARD_TLS=0`) on a bind
|
|
586
|
+
that crosses the LAN, the startup log warns that the session cookie crosses the network in
|
|
587
|
+
clear text. Pair that with a TLS-terminating proxy, or with a loopback bind and an SSH tunnel.
|
|
584
588
|
|
|
585
589
|
Accounts plus two node writes that operators may run:
|
|
586
590
|
|
|
@@ -596,7 +600,8 @@ Accounts plus two node writes that operators may run:
|
|
|
596
600
|
|
|
597
601
|
### TLS
|
|
598
602
|
|
|
599
|
-
|
|
603
|
+
HTTPS is on by default, with a certificate the server makes for itself. A certificate of your
|
|
604
|
+
own, on every listener:
|
|
600
605
|
|
|
601
606
|
```json
|
|
602
607
|
{
|
|
@@ -632,9 +637,9 @@ matters here too: it makes the proxy the only way in.
|
|
|
632
637
|
|
|
633
638
|
### Binding to specific addresses
|
|
634
639
|
|
|
635
|
-
The default `0.0.0.0` listens on every IPv4
|
|
636
|
-
tunnels and container bridges. To serve exactly
|
|
637
|
-
address, and admit only clients from those networks:
|
|
640
|
+
The default `127.0.0.1` answers this machine only. `0.0.0.0` listens on every IPv4
|
|
641
|
+
interface the machine has, including VPN tunnels and container bridges. To serve exactly
|
|
642
|
+
one LAN address and one VPN address, and admit only clients from those networks:
|
|
638
643
|
|
|
639
644
|
```json
|
|
640
645
|
{
|
|
@@ -661,9 +666,11 @@ Things to know about specific binds:
|
|
|
661
666
|
- For this machine only, use `"hosts": ["127.0.0.1"]` and reach it with
|
|
662
667
|
`ssh -L 21000:127.0.0.1:21000 user@monitor-host`.
|
|
663
668
|
|
|
664
|
-
### Markets off
|
|
669
|
+
### Markets off, for good
|
|
665
670
|
|
|
666
|
-
|
|
671
|
+
Out of the box there are no outbound connections except to the node, because market polling is
|
|
672
|
+
a checkbox that ships unticked (**Display settings → Markets & Price → Enable market polling**). To be certain a machine never reaches out whatever anyone
|
|
673
|
+
ticks, remove the feed from the server:
|
|
667
674
|
|
|
668
675
|
```json
|
|
669
676
|
{
|
|
@@ -713,7 +720,7 @@ sudo systemctl edit blockyard
|
|
|
713
720
|
|
|
714
721
|
```ini
|
|
715
722
|
[Service]
|
|
716
|
-
Environment=
|
|
723
|
+
Environment=BLOCKYARD_BIND=192.0.2.10
|
|
717
724
|
Environment=BLOCKYARD_DATA=/var/lib/blockyard
|
|
718
725
|
```
|
|
719
726
|
|
package/docs/GETTING-STARTED.md
CHANGED
|
@@ -158,12 +158,15 @@ existing `config/local.json` (with a backup).
|
|
|
158
158
|
npm start
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
-
The log says `BlockYard 0.0
|
|
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
|
|
162
165
|
/Users/you/blockyard/data/index from main's block files with 4 workers -- the Overview shows the
|
|
163
166
|
progress` (and `address index build: paused while the node's RPC is answering in … s` /
|
|
164
167
|
`resumed` if the node struggles) and, when that is done, `address index built: … rows to block N
|
|
165
168
|
in … min -- address pages are live` followed by `address index /Users/you/blockyard/data/index:
|
|
166
|
-
following main from block N`. Open <
|
|
169
|
+
following main from block N`. Open <https://127.0.0.1:21000> and sign in as `admin`. The
|
|
167
170
|
Overview fills in within about thirty seconds; Block space lands a little after. Open Explorer,
|
|
168
171
|
click the latest block, then any output address: while the index is building the page says so
|
|
169
172
|
with the progress; once it is built, its balance, history and unspent outputs appear.
|
package/docs/INSTALL.md
CHANGED
|
@@ -12,7 +12,7 @@ machines you choose. Every setting mentioned here is described in full in
|
|
|
12
12
|
- [6. Run it as a service](#6-run-it-as-a-service)
|
|
13
13
|
- [7. Decide who can reach it](#7-decide-who-can-reach-it)
|
|
14
14
|
- [8. Accounts (optional)](#8-accounts-optional)
|
|
15
|
-
- [9. HTTPS (
|
|
15
|
+
- [9. HTTPS (on by default)](#9-https-on-by-default)
|
|
16
16
|
- [10. Behind a reverse proxy (optional)](#10-behind-a-reverse-proxy-optional)
|
|
17
17
|
- [11. Updating](#11-updating)
|
|
18
18
|
- [12. Uninstalling](#12-uninstalling)
|
|
@@ -146,9 +146,10 @@ Balances are checked against the node: 40 of 40 sampled addresses equal `scantxo
|
|
|
146
146
|
satoshi (`node scripts/index-benchmark.js` runs that check and the lookup timings against your
|
|
147
147
|
own build).
|
|
148
148
|
|
|
149
|
-
**Outbound network access
|
|
150
|
-
explorer's dollar figures (HTTPS to five
|
|
151
|
-
|
|
149
|
+
**Outbound network access**: none, out of the box — everything talks only to your node. The
|
|
150
|
+
Markets and Kiosk tabs and the explorer's dollar figures need the exchange feed (HTTPS to five
|
|
151
|
+
exchanges' public APIs), which is off until you tick **Display settings → Markets & Price → Enable market polling**
|
|
152
|
+
in the browser. See [SECURITY.md](SECURITY.md#outbound-connections).
|
|
152
153
|
|
|
153
154
|
## 2. Get the code
|
|
154
155
|
|
|
@@ -240,8 +241,8 @@ real-time explorer data over RPC was a failed idea.
|
|
|
240
241
|
|
|
241
242
|
`rpcUser` / `rpcPassword` are still accepted, for a node that authenticates with `rpcauth` rather
|
|
242
243
|
than the cookie file (see [Configuration](CONFIGURATION.md#nodes)). The web UI's node-connection
|
|
243
|
-
form takes no password on purpose -- taking one over
|
|
244
|
-
|
|
244
|
+
form takes no password on purpose -- taking one over a web endpoint is not something to add
|
|
245
|
+
quietly -- so credentials go in `config/local.json`.
|
|
245
246
|
|
|
246
247
|
##### `bitcoin.conf` settings worth having
|
|
247
248
|
|
|
@@ -284,17 +285,20 @@ npm run check # every configured node: RPC, credentials, txindex, getblock
|
|
|
284
285
|
npm start
|
|
285
286
|
```
|
|
286
287
|
|
|
287
|
-
Watch the start-up lines. You should see
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
288
|
+
Watch the start-up lines. You should see `listening on https://127.0.0.1:21000` (the first start
|
|
289
|
+
makes the monitor its own self-signed certificate under `data/tls/`; the browser warns once per
|
|
290
|
+
address and remembers it), a line per node, and — because accounts are on by default — `created
|
|
291
|
+
the first admin account (admin)` with a generated password **shown once** (set
|
|
292
|
+
`BLOCKYARD_ADMIN_PASSWORD` before the first start to choose it; `blockyard user` changes it
|
|
293
|
+
later). If the node entry names an `addressIndex` directory with no index in it, `address index:
|
|
294
|
+
building … with N workers -- the Overview shows the progress` follows, and the build runs on in
|
|
295
|
+
the background (see [Building the address index](#building-the-address-index)). Then open
|
|
296
|
+
<https://127.0.0.1:21000> and sign in.
|
|
293
297
|
|
|
294
|
-
Check it from the shell:
|
|
298
|
+
Check it from the shell (`-k`, because the certificate is self-signed):
|
|
295
299
|
|
|
296
300
|
```bash
|
|
297
|
-
curl -
|
|
301
|
+
curl -sk https://127.0.0.1:21000/api/health
|
|
298
302
|
```
|
|
299
303
|
|
|
300
304
|
If a node shows as offline, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md#a-node-shows-offline).
|
|
@@ -356,10 +360,10 @@ Where the monitor listens is a security decision. Set `server.host` in `config/l
|
|
|
356
360
|
|
|
357
361
|
| bind | who can connect | typical use |
|
|
358
362
|
|---|---|---|
|
|
359
|
-
| `"127.0.0.1"` | this machine only | reach it with an SSH tunnel: `ssh -L 21000:127.0.0.1:21000 you@host` |
|
|
363
|
+
| `"127.0.0.1"` (the default) | this machine only | reach it with an SSH tunnel: `ssh -L 21000:127.0.0.1:21000 you@host` |
|
|
360
364
|
| `"192.0.2.10"` (a LAN address) | anything that can route to that address | a home or office LAN |
|
|
361
365
|
| `["192.0.2.10", "198.51.100.7"]` | exactly those addresses | LAN plus a VPN such as Tailscale or WireGuard |
|
|
362
|
-
| `"0.0.0.0"`
|
|
366
|
+
| `"0.0.0.0"` | every interface | behind a firewall you control |
|
|
363
367
|
|
|
364
368
|
Notes:
|
|
365
369
|
|
|
@@ -377,15 +381,17 @@ Notes:
|
|
|
377
381
|
- `server.allowCidrs` (or `BLOCKYARD_ALLOW_CIDRS=192.0.2.0/24,2001:db8::/32`) makes the
|
|
378
382
|
monitor itself refuse clients outside those networks, as a second line of defence.
|
|
379
383
|
|
|
380
|
-
## 8. Accounts (
|
|
384
|
+
## 8. Accounts (on by default)
|
|
381
385
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
386
|
+
Sign-in is required out of the box. The first start with an empty data directory creates an
|
|
387
|
+
`admin` account and prints its password **once** in the log (under systemd: `journalctl -u
|
|
388
|
+
blockyard`). Set your own instead with `BLOCKYARD_ADMIN_PASSWORD` for that first start.
|
|
385
389
|
|
|
386
|
-
To
|
|
387
|
-
|
|
388
|
-
|
|
390
|
+
To open the monitor to readers with no account, set `"auth": { "enabled": false }` or
|
|
391
|
+
`BLOCKYARD_AUTH=0` and restart: anyone who can reach the port then reads it as a `viewer` —
|
|
392
|
+
charts, the explorer, the event stream, the read-only RPC console — while user
|
|
393
|
+
administration, the audit trail and every node write stay closed. The boot log names the
|
|
394
|
+
addresses that leaves readable.
|
|
389
395
|
|
|
390
396
|
Manage accounts from the Admin page, or from the shell — for example, to reset the admin
|
|
391
397
|
password:
|
|
@@ -398,17 +404,35 @@ Roles: `viewer` reads; `operator` may also run node actions that you have enable
|
|
|
398
404
|
also manages users and reads the audit trail. Node actions are off unless you enable them
|
|
399
405
|
explicitly — see [SECURITY.md](SECURITY.md#node-writes).
|
|
400
406
|
|
|
401
|
-
## 9. HTTPS (
|
|
407
|
+
## 9. HTTPS (on by default)
|
|
402
408
|
|
|
403
|
-
|
|
409
|
+
Every listener serves HTTPS out of the box. With no certificate of your own named, the first
|
|
410
|
+
start makes one: a self-signed certificate and key under `<data>/tls/` (`data/tls/cert.pem` and
|
|
411
|
+
`key.pem`, the key readable by the service account only), naming the addresses the monitor is
|
|
412
|
+
reached on — the bound hosts, this machine's addresses and hostname, `localhost`. It is remade
|
|
413
|
+
by itself when it nears expiry (825 days) or stops naming a bound address. Browsers warn once
|
|
414
|
+
per address about a self-signed certificate and then remember it; the start-up log prints its
|
|
415
|
+
fingerprint so you can compare.
|
|
416
|
+
|
|
417
|
+
To add a name or address (say, a DNS name you gave the machine) or start over:
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
blockyard tls --san monitor.lan.example # or: node scripts/tls.js --san ...
|
|
421
|
+
blockyard tls --force # a fresh key and certificate
|
|
422
|
+
blockyard tls --print > blockyard.crt # the certificate, for another machine's trust store
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
To use a certificate of your own instead, name it and every listener serves that:
|
|
404
426
|
|
|
405
427
|
```bash
|
|
406
428
|
BLOCKYARD_TLS_CERT=/etc/blockyard/cert.pem BLOCKYARD_TLS_KEY=/etc/blockyard/key.pem npm start
|
|
407
429
|
```
|
|
408
430
|
|
|
409
|
-
or in `config/local.json`: `"server": { "tls": { "cert": "...", "key": "..." } }`.
|
|
431
|
+
or in `config/local.json`: `"server": { "tls": { "cert": "...", "key": "..." } }`. To serve plain
|
|
432
|
+
HTTP — behind a reverse proxy that terminates TLS (section 10) — set `BLOCKYARD_TLS=0` or
|
|
433
|
+
`"server": { "tls": { "enabled": false } }`.
|
|
410
434
|
|
|
411
|
-
|
|
435
|
+
If you would rather make the certificate with openssl yourself:
|
|
412
436
|
|
|
413
437
|
```bash
|
|
414
438
|
sudo mkdir -p /etc/blockyard
|
|
@@ -424,8 +448,8 @@ rather than silently serving plain HTTP.
|
|
|
424
448
|
|
|
425
449
|
## 10. Behind a reverse proxy (optional)
|
|
426
450
|
|
|
427
|
-
If you already run nginx, Caddy or similar, bind the monitor to `127.0.0.1
|
|
428
|
-
terminate TLS. The live stream (`/api/stream`) is Server-Sent Events, so the proxy must not
|
|
451
|
+
If you already run nginx, Caddy or similar, bind the monitor to `127.0.0.1`, set
|
|
452
|
+
`BLOCKYARD_TLS=0` so it speaks plain HTTP to the proxy, and let the proxy terminate TLS. The live stream (`/api/stream`) is Server-Sent Events, so the proxy must not
|
|
429
453
|
buffer it. An nginx example:
|
|
430
454
|
|
|
431
455
|
```nginx
|
|
@@ -468,8 +492,41 @@ Your `config/local.json` and `data/` directory are untouched by updates. Read
|
|
|
468
492
|
up new front-end files on the next page load; the header shows a notice when the page you
|
|
469
493
|
have open is older than the server.
|
|
470
494
|
|
|
495
|
+
**Updating from 0.0.9.** The defaults hardened in 0.1.0, and a `config/local.json` written by
|
|
496
|
+
0.0.9's installer does not name them, so the first start after the update behaves like a fresh
|
|
497
|
+
install in three ways:
|
|
498
|
+
|
|
499
|
+
- **HTTPS.** The monitor makes itself a self-signed certificate under `data/tls/` and serves
|
|
500
|
+
HTTPS on the same port; `http://…:21000` stops answering. Open `https://`, accept the
|
|
501
|
+
certificate once. Behind your own reverse proxy, set `BLOCKYARD_TLS=0` (§10).
|
|
502
|
+
- **Sign-in.** Accounts are on. The first start creates the `admin` account and prints its
|
|
503
|
+
password **once** in the log (`journalctl -u blockyard` under systemd); set
|
|
504
|
+
`BLOCKYARD_ADMIN_PASSWORD` before that start to choose it. To keep the monitor open as before,
|
|
505
|
+
put `"auth": { "enabled": false }` in `config/local.json` or start with `BLOCKYARD_AUTH=0`.
|
|
506
|
+
- **This machine only.** With no `server.host` in the config the bind is `127.0.0.1`. A
|
|
507
|
+
0.0.9 config written by the installer names the host it chose, so a LAN bind stays; if yours
|
|
508
|
+
does not, add `BLOCKYARD_BIND` or `server.hosts` (§7).
|
|
509
|
+
- **Market polling is off** until someone ticks **Display settings → Markets & Price → Enable
|
|
510
|
+
market polling** — once, for every screen.
|
|
511
|
+
|
|
471
512
|
## 12. Uninstalling
|
|
472
513
|
|
|
514
|
+
BlockYard touches nothing outside its own directories: it reads `bitcoin.conf` and never writes
|
|
515
|
+
it, and it leaves the node as it found it. Removing it is deleting those directories.
|
|
516
|
+
|
|
517
|
+
**Installed from npm** (`npm install -g blockyard`): stop it, then
|
|
518
|
+
|
|
519
|
+
```bash
|
|
520
|
+
npm uninstall -g blockyard
|
|
521
|
+
rm -rf ~/.blockyard # local.json, data/ (history, accounts, the audit trail) and data/index
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
**A checkout** run with `npm start`: stop it and delete the checkout; `config/` and `data/`,
|
|
525
|
+
the index included, live inside it. If you pointed the index elsewhere at setup time
|
|
526
|
+
(`addressIndex` in `config/local.json`), delete that directory too.
|
|
527
|
+
|
|
528
|
+
**The systemd service** of section 6:
|
|
529
|
+
|
|
473
530
|
```bash
|
|
474
531
|
sudo systemctl disable --now blockyard
|
|
475
532
|
sudo rm /etc/systemd/system/blockyard.service && sudo systemctl daemon-reload
|
|
@@ -484,7 +541,7 @@ sudo userdel blockyard
|
|
|
484
541
|
- [ ] `npm run check` passes: Core 25.0+, `txindex` synced, the block files readable, no pruning
|
|
485
542
|
- [ ] the index directory has ~125 GB free and is writable by the service account
|
|
486
543
|
- [ ] the start-up log shows the addresses you intended, and no node offline
|
|
487
|
-
- [ ] you have decided who can reach the port (bind, firewall, `allowCidrs`)
|
|
488
|
-
- [ ] accounts on if the port is reachable by people who should not see your node
|
|
489
|
-
- [ ] HTTPS on, or a proxy / tunnel in front, if
|
|
490
|
-
- [ ] `BLOCKYARD_MARKETS=0`
|
|
544
|
+
- [ ] you have decided who can reach the port: it binds `127.0.0.1` until you say otherwise (bind, firewall, `allowCidrs`)
|
|
545
|
+
- [ ] accounts stay on (the default) if the port is reachable by people who should not see your node; you have the admin password from the first start
|
|
546
|
+
- [ ] HTTPS is on by default with the monitor's own certificate; a certificate of your own, or a proxy / tunnel in front, if you want no browser warning
|
|
547
|
+
- [ ] **Enable market polling** ticked in Display settings if you want the Markets and Kiosk tabs (off by default); `BLOCKYARD_MARKETS=0` on machines that must make no outbound connections at all
|