blockyard 0.0.1 → 0.0.9
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 +679 -0
- package/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +172 -4
- package/SECURITY.md +38 -0
- package/bin/blockyard.js +40 -0
- package/config/pool-map.json +2620 -0
- package/docs/API.md +1575 -0
- package/docs/ARCHITECTURE.md +1307 -0
- package/docs/AUTO-UPDATE.md +269 -0
- package/docs/CONFIGURATION.md +840 -0
- package/docs/DEFECTS.md +813 -0
- package/docs/EFFECTS-AGENTS.md +448 -0
- package/docs/GETTING-STARTED.md +202 -0
- package/docs/INSTALL.md +490 -0
- package/docs/MEASUREMENTS.md +1254 -0
- package/docs/PRIVATE-LEADERBOARD.md +230 -0
- package/docs/RULES.md +681 -0
- package/docs/SECURITY-AUDIT-2026-09-14.md +177 -0
- package/docs/SECURITY-AUDIT.md +258 -0
- package/docs/SECURITY.md +195 -0
- package/docs/STATE-2026-09-09.md +200 -0
- package/docs/TROUBLESHOOTING.md +298 -0
- package/docs/USER-GUIDE.md +1022 -0
- package/package.json +53 -5
- package/public/404.html +9 -0
- package/public/css/app.css +1785 -0
- package/public/index.html +893 -0
- package/public/js/about.js +112 -0
- package/public/js/agents.js +964 -0
- package/public/js/app.js +1312 -0
- package/public/js/arkanoid.js +806 -0
- package/public/js/blockanoid.js +347 -0
- package/public/js/blockout.js +347 -0
- package/public/js/blockpack.js +428 -0
- package/public/js/blockscene3d.js +2678 -0
- package/public/js/breakout.js +224 -0
- package/public/js/charts.js +635 -0
- package/public/js/depthchart.js +311 -0
- package/public/js/details3d.js +2957 -0
- package/public/js/explorer.js +405 -0
- package/public/js/feepalette.js +149 -0
- package/public/js/fmt.js +162 -0
- package/public/js/goggles.js +886 -0
- package/public/js/kiosk.js +41 -0
- package/public/js/login.js +83 -0
- package/public/js/markets.js +357 -0
- package/public/js/mining.js +1138 -0
- package/public/js/panels.js +966 -0
- package/public/js/pricechart.js +188 -0
- package/public/js/settings.js +1014 -0
- package/public/js/tetris.js +226 -0
- package/public/js/tetrust.js +356 -0
- package/public/js/tetsound.js +175 -0
- package/public/login.html +33 -0
- package/scripts/blockfile-measure.js +156 -0
- package/scripts/browser-check.mjs +286 -0
- package/scripts/check.js +173 -0
- package/scripts/decode-check.js +81 -0
- package/scripts/doc-counts.js +109 -0
- package/scripts/donate-qr.py +20 -0
- package/scripts/fake-node.js +534 -0
- package/scripts/index-bench.js +216 -0
- package/scripts/index-benchmark.js +117 -0
- package/scripts/index-build.js +40 -0
- package/scripts/live-render-check.mjs +89 -0
- package/scripts/manage-users.js +132 -0
- package/scripts/motion-check.mjs +138 -0
- package/scripts/pool-map.js +157 -0
- package/scripts/setup.js +410 -0
- package/scripts/shots.mjs +272 -0
- package/scripts/smoke.sh +327 -0
- package/scripts/ui.js +174 -0
- package/server/auth/sessions.js +221 -0
- package/server/auth/users.js +243 -0
- package/server/chain/blockfile.js +234 -0
- package/server/chain/index/build.js +193 -0
- package/server/chain/index/heights.js +36 -0
- package/server/chain/index/live.js +276 -0
- package/server/chain/index/rows.js +145 -0
- package/server/chain/index/store.js +154 -0
- package/server/chain/index/worker.js +109 -0
- package/server/chain/tx.js +310 -0
- package/server/collect/gbt.js +229 -0
- package/server/collect/logparse.js +765 -0
- package/server/collect/logtail.js +189 -0
- package/server/collect/markets.js +333 -0
- package/server/collect/mining.js +333 -0
- package/server/collect/monitor.js +2516 -0
- package/server/collect/nextblock.js +275 -0
- package/server/collect/sync.js +386 -0
- package/server/config.js +620 -0
- package/server/http/api.js +1275 -0
- package/server/http/explorer.js +418 -0
- package/server/http/server.js +412 -0
- package/server/http/sse.js +176 -0
- package/server/http/static.js +212 -0
- package/server/main.js +628 -0
- package/server/netinfo.js +253 -0
- package/server/rpc/allowlist.js +130 -0
- package/server/rpc/client.js +414 -0
- package/server/store/audit.js +148 -0
- package/server/store/history.js +220 -0
- package/server/store/ledger.js +290 -0
- package/server/store/ring.js +173 -0
- package/server/util/fmt.js +29 -0
- package/systemd/blockyard.service +100 -0
package/docs/INSTALL.md
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
# Installing BlockYard
|
|
2
|
+
|
|
3
|
+
This guide takes you from nothing to a monitor running as a service, reachable from the
|
|
4
|
+
machines you choose. Every setting mentioned here is described in full in
|
|
5
|
+
[CONFIGURATION.md](CONFIGURATION.md).
|
|
6
|
+
|
|
7
|
+
- [1. Requirements](#1-requirements)
|
|
8
|
+
- [2. Get the code](#2-get-the-code)
|
|
9
|
+
- [3. Try it without a node](#3-try-it-without-a-node)
|
|
10
|
+
- [4. Point it at your node](#4-point-it-at-your-node)
|
|
11
|
+
- [5. First run](#5-first-run)
|
|
12
|
+
- [6. Run it as a service](#6-run-it-as-a-service)
|
|
13
|
+
- [7. Decide who can reach it](#7-decide-who-can-reach-it)
|
|
14
|
+
- [8. Accounts (optional)](#8-accounts-optional)
|
|
15
|
+
- [9. HTTPS (optional)](#9-https-optional)
|
|
16
|
+
- [10. Behind a reverse proxy (optional)](#10-behind-a-reverse-proxy-optional)
|
|
17
|
+
- [11. Updating](#11-updating)
|
|
18
|
+
- [12. Uninstalling](#12-uninstalling)
|
|
19
|
+
- [Checklist](#checklist)
|
|
20
|
+
|
|
21
|
+
## 1. Requirements
|
|
22
|
+
|
|
23
|
+
| need | notes |
|
|
24
|
+
|---|---|
|
|
25
|
+
| **Node.js 22 or newer** | `node -v` must print `v22` or later. Older runtimes fail on syntax at start-up, which looks like a bug in the app. Install from [nodejs.org](https://nodejs.org), your distribution's backports, or a version manager such as `nvm`. |
|
|
26
|
+
| **Bitcoin Core 25.0 or later** | [Bitcoin Core](https://github.com/bitcoin/bitcoin) with `server=1` and `txindex=1`, **on the same machine** as BlockYard, which reads the node's block files for the explorer's address index. A node on another machine is not supported. 25.0 is where `getblock` verbosity 3, which the index follower uses, arrived; 29.1 is what the macOS install was done against. `coinstatsindex=1` is optional (without it the UTXO figures are blank and the node is not asked for them). Not a pruned node: the index needs every block file. |
|
|
27
|
+
| **RPC credentials** | Either read access to the node's cookie file (`<datadir>/<chain>/.cookie`, the usual case on the same machine) or an RPC user and password. |
|
|
28
|
+
| **macOS or Linux** | There is nothing to compile, and the server calls no platform-specific API (no `child_process`, no `/proc`, no `systemctl`). Developed on Linux; a real install has been done on macOS (Core 29.1). The test suite runs in CI on Ubuntu, macOS and Windows (Node 22 and 24), but on Windows nothing more than the suite has been tried. Only the *service* instructions in section 6 are Linux-specific (they use systemd); on macOS run it in a terminal, or write a `launchd` plist. The index store opens its files per lookup, so macOS's default limit of 256 open files is enough. |
|
|
29
|
+
| **Disk** | About **125 GB** for the address index (124 GB measured at height 966,930, growing ~55 MB a day), on top of the node's own ~875 GB of block files, which the build reads once. A different disk from the node's is best. A few hundred MB besides for history, sessions and the audit trail (`./data` by default). |
|
|
30
|
+
| **Memory** | About 2.5 GB per index-build worker while the build runs (four by default); little after. |
|
|
31
|
+
| **A modern browser** | Any current Chrome, Edge, Firefox or Safari. The 3D views use a 2D canvas and run without WebGL; a GPU helps with the dense viewer mode. |
|
|
32
|
+
|
|
33
|
+
### Node indexes
|
|
34
|
+
|
|
35
|
+
**`txindex=1` is required for the explorer's transaction pages.** A transaction page asks the node
|
|
36
|
+
for `getrawtransaction <txid> 2` with no block hash, and a node without a transaction index can
|
|
37
|
+
only answer that for transactions still in its mempool — so without it, looking up a confirmed
|
|
38
|
+
transaction by id fails even though the node is perfectly healthy. Set it in `bitcoin.conf`:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
txindex=1
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Adding it to a node that has been running without it triggers a one-off reindex, which takes a
|
|
45
|
+
while and is unavoidable; the node reports progress, and `getindexinfo` tells you when it is
|
|
46
|
+
`synced`. Everything else in the monitor — the dashboard, block space, mempool, fees, peers,
|
|
47
|
+
mining, the block pages — works without it.
|
|
48
|
+
|
|
49
|
+
Two further points affect the explorer:
|
|
50
|
+
|
|
51
|
+
- an **address index** — address pages (balance, received, sent, transaction history). **Bitcoin
|
|
52
|
+
Core does not have one, at any setting**, so there is no flag here to turn on: `getaddressbalance`
|
|
53
|
+
and `getaddresstxids` are insight-style extensions carried by forks such as Bitcore, and stock
|
|
54
|
+
Core answers `Method not found` (measured 2026-09-13 against two Core nodes). **BlockYard builds its own** from the node's block files — see
|
|
55
|
+
[Building the address index](#building-the-address-index) below. Without one, the address page
|
|
56
|
+
still confirms an address and its type (`validateaddress` needs no index) and marks balance and
|
|
57
|
+
history as *not indexed*. Searching by transaction id or block is unaffected: that uses
|
|
58
|
+
`txindex` above.
|
|
59
|
+
- **"spent by" links** come from `gettxspendingprevout`, which Core (24.0 and later) answers from
|
|
60
|
+
its **mempool** only: an output spent by an unconfirmed transaction is linked, one spent in a
|
|
61
|
+
block is not, and there is no index to turn on for that.
|
|
62
|
+
|
|
63
|
+
Pages that need an index the node does not have say so, rather than showing empty data.
|
|
64
|
+
|
|
65
|
+
### Building the address index
|
|
66
|
+
|
|
67
|
+
**By default BlockYard builds it itself**, in the background, the first time it starts with an
|
|
68
|
+
`addressIndex` directory that holds no index. The build runs on worker threads inside the server
|
|
69
|
+
while every page keeps working: the Overview's "What this panel cannot tell you" box shows the
|
|
70
|
+
progress (phase, files done, rows so far, an ETA that settles after the first few files), the
|
|
71
|
+
address page says the same in place of a history, and an event — which the browser shows as a
|
|
72
|
+
notification — marks the start, the finish and a failure. When it finishes the follower starts
|
|
73
|
+
on the spot, so address pages work without a restart. Stopping BlockYard stops the build; there
|
|
74
|
+
is no resume, so the next start begins it again. Three keys on the node entry control it:
|
|
75
|
+
|
|
76
|
+
| key | meaning |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `addressIndex` | the index directory (the installer's default is `data/index` inside the checkout) |
|
|
79
|
+
| `addressIndexWorkers` | how many worker threads the build uses; the installer writes the number you gave it (default 4, never more than 4 by default). Without the key the server uses half of what a dedicated build would, at most four |
|
|
80
|
+
| `addressIndexBuild: "manual"` | do not build automatically; the installer's **(l)ater** writes this. Run the command below yourself and restart |
|
|
81
|
+
|
|
82
|
+
**The build is paced by the node.** Its workers read the block files the node is also reading,
|
|
83
|
+
so before each file it looks at the monitor's own RPC telemetry: while the node's RPC is failing
|
|
84
|
+
or averaging above the monitor's slow threshold (`rpc.slowLatencyMs`, 5 s by default) it holds,
|
|
85
|
+
checking every 10 s; while merely slow it eases off. The progress line and the log say when it is
|
|
86
|
+
paused and when it resumed. The build's own RPC calls (cheap: block hashes for the height table)
|
|
87
|
+
go over a second connection so they are not queued behind the monitor's mempool and block reads.
|
|
88
|
+
On **spinning disks** use one worker (`addressIndexWorkers: 1`, or answer 1 to the installer):
|
|
89
|
+
parallel readers only seek against each other and against the node, and the build takes hours
|
|
90
|
+
there whatever the number.
|
|
91
|
+
|
|
92
|
+
The index is built once from the node's own `blocks/blk*.dat` and `rev*.dat` files, so the
|
|
93
|
+
build needs to run **on a machine that can read the node's data directory** -- the node's own
|
|
94
|
+
machine, which is where BlockYard runs. After that the server keeps it current over RPC.
|
|
95
|
+
|
|
96
|
+
**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.
|
|
97
|
+
|
|
98
|
+
What it costs, measured on the full chain at height 966,930 (`docs/MEASUREMENTS.md` §30):
|
|
99
|
+
**29 min 45 s** with 16 workers on NVMe (7.8 CPU-hours; peak 30 GB of memory, so about 2.5 GB
|
|
100
|
+
per worker), and **124 GB** of disk for 5.89 billion 21-byte rows, one per (address, transaction)
|
|
101
|
+
with the net amount, so a balance is a sum and never a node call; it grows about 55 MB a day. It
|
|
102
|
+
stores no transactions — `txindex` does that — which is why it is a tenth the size of
|
|
103
|
+
mempool.space's `electrs` (1.3 TB, hours to build). Put it on a different disk from the block
|
|
104
|
+
files if you can; the build reads ~880 GB once.
|
|
105
|
+
|
|
106
|
+
To build by hand (the installer's **(h)ere** runs the same build in the terminal, with the same
|
|
107
|
+
pacing):
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
node scripts/index-build.js --out data/index --workers 4
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Progress goes to stderr once a second; the manifest, with every phase's timings, to stdout at
|
|
114
|
+
the end. Each worker holds its file pair and a row buffer, so memory scales with `--workers`
|
|
115
|
+
(the 16-worker build above peaked at 30 GB): on a 16-32 GB machine use `--workers 4`, which
|
|
116
|
+
takes roughly four times as long. The block files are found through the node's `datadir` in
|
|
117
|
+
`config/local.json` (`<datadir>/blocks`), so that must be set -- on macOS Core's default is
|
|
118
|
+
`~/Library/Application Support/Bitcoin`. Then name the directory in the node's config and restart:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"nodes": [
|
|
123
|
+
{ "id": "main", "...": "...", "addressIndex": "/opt/blockyard/data/index", "addressIndexWorkers": 4 }
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
One index serves every node on the same chain. The server starts a follower per directory,
|
|
129
|
+
which polls every 30 s, fetches each new block with `getblock <hash> 3` (up to 50 blocks a poll
|
|
130
|
+
when catching up), and writes `live.log` and `layers/` **inside the index directory — so it must
|
|
131
|
+
be writable by the service user**. A restart replays the log; a reorganisation rolls the tail
|
|
132
|
+
back; blocks 100 deep are folded into sorted layers. The address page says when the index is
|
|
133
|
+
behind the node or has stopped following, and rows above the node's current tip are never shown
|
|
134
|
+
as history. A reorganisation deeper than the tail it holds (100 blocks) cannot be repaired in
|
|
135
|
+
place: the page says to rebuild: stop the server, run the same command again (or delete the
|
|
136
|
+
directory and let the server build it), start it. **The build empties `--out` first**, the
|
|
137
|
+
follower's log and layers included, so nothing of the old index survives it (tested in
|
|
138
|
+
`test/chain-index-live.test.js`).
|
|
139
|
+
|
|
140
|
+
What the address page shows from it: the history, the balance, received and sent (each
|
|
141
|
+
transaction's net for the address), and — for an address with up to 100 transactions — its
|
|
142
|
+
unspent outputs, checked one by one against the node's `gettxout`; a longer history gets a note
|
|
143
|
+
instead. Not yet: an address's mempool transactions.
|
|
144
|
+
|
|
145
|
+
Balances are checked against the node: 40 of 40 sampled addresses equal `scantxoutset` to the
|
|
146
|
+
satoshi (`node scripts/index-benchmark.js` runs that check and the lookup timings against your
|
|
147
|
+
own build).
|
|
148
|
+
|
|
149
|
+
**Outbound network access** is needed only for the Markets and Kiosk tabs and for the
|
|
150
|
+
explorer's dollar figures (HTTPS to five exchanges' public APIs). Everything else talks only
|
|
151
|
+
to your node. See [SECURITY.md](SECURITY.md#outbound-connections).
|
|
152
|
+
|
|
153
|
+
## 2. Get the code
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
git clone https://github.com/BobClawblaw/blockyard.git
|
|
157
|
+
cd blockyard
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
There is **no `npm install`** — the project has no dependencies. Optionally confirm your
|
|
161
|
+
runtime is good:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
npm test
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## 3. Try it without a node
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
npm run dev
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
This starts the monitor on <http://127.0.0.1:18088> against a built-in fake node that
|
|
174
|
+
simulates a node syncing, with no configuration file read. It is the quickest way to see
|
|
175
|
+
every page, and it is what the test suite uses. Stop it with `Ctrl-C`.
|
|
176
|
+
|
|
177
|
+
## 4. Point it at your node
|
|
178
|
+
|
|
179
|
+
**The short way:** `npm run setup` asks for the node's data directory first and reads its
|
|
180
|
+
`bitcoin.conf` (chain, `rpcport`, `rpcconnect`, `rpcuser`/`rpcpassword`, `rpcauth` users, a cookie
|
|
181
|
+
file elsewhere, `server=`, `txindex=`, `prune=`, chain sections, `includeconf=`), so the RPC URL
|
|
182
|
+
and credentials arrive as defaults; checks them against the node (RPC, credentials, chain,
|
|
183
|
+
`txindex`, `getblock 3`, a verbose mempool read, the block files, the log -- every call timed);
|
|
184
|
+
asks bind address, port, index directory and workers; writes `config/local.json`; and lets
|
|
185
|
+
BlockYard build the address index in the background when it starts --
|
|
186
|
+
[GETTING-STARTED.md](GETTING-STARTED.md). What follows is the same configuration by hand, and
|
|
187
|
+
what each key means.
|
|
188
|
+
|
|
189
|
+
Create `config/local.json` (it is git-ignored, so your settings never end up in a commit).
|
|
190
|
+
|
|
191
|
+
**Same machine, cookie authentication** — the common case. The monitor finds the cookie
|
|
192
|
+
at `<datadir>/<chainHint>/.cookie`:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"nodes": [
|
|
197
|
+
{
|
|
198
|
+
"id": "main",
|
|
199
|
+
"label": "My node",
|
|
200
|
+
"rpcUrl": "http://127.0.0.1:8332",
|
|
201
|
+
"datadir": "/home/you/.bitcoin",
|
|
202
|
+
"chainHint": "main",
|
|
203
|
+
"addressIndex": "/home/you/blockyard/data/index",
|
|
204
|
+
"addressIndexWorkers": 4
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**User/password authentication** -- a node that uses `rpcauth` instead of the cookie file. Keep
|
|
211
|
+
`datadir`: the block files are still read from it.
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"nodes": [
|
|
216
|
+
{
|
|
217
|
+
"id": "main",
|
|
218
|
+
"label": "My node",
|
|
219
|
+
"rpcUrl": "http://127.0.0.1:8332",
|
|
220
|
+
"datadir": "/home/you/.bitcoin",
|
|
221
|
+
"rpcUser": "monitor",
|
|
222
|
+
"rpcPassword": "a long random password"
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Use the RPC port your node is configured with (`rpcport` in its configuration file; Core's
|
|
229
|
+
mainnet default is 8332). Any value you set in `config/local.json` or in the environment
|
|
230
|
+
overrides the built-in defaults.
|
|
231
|
+
|
|
232
|
+
#### It runs on the node's machine
|
|
233
|
+
|
|
234
|
+
BlockYard is installed **on the machine that runs Bitcoin Core**, and nowhere else. The explorer's
|
|
235
|
+
address index is built from the node's own block files (`<datadir>/blocks`), the way
|
|
236
|
+
mempool.space's `electrs` does it ([Building the address index](#building-the-address-index)),
|
|
237
|
+
because Core cannot answer an address's history over RPC at any setting. A node on another
|
|
238
|
+
machine is **not supported**: reading one over RPC alone was tried (2026-09-13) and dropped --
|
|
239
|
+
real-time explorer data over RPC was a failed idea.
|
|
240
|
+
|
|
241
|
+
`rpcUser` / `rpcPassword` are still accepted, for a node that authenticates with `rpcauth` rather
|
|
242
|
+
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 an endpoint that is open by default is not
|
|
244
|
+
something to add quietly -- so credentials go in `config/local.json`.
|
|
245
|
+
|
|
246
|
+
##### `bitcoin.conf` settings worth having
|
|
247
|
+
|
|
248
|
+
```conf
|
|
249
|
+
txindex=1 # required for the explorer's transaction pages
|
|
250
|
+
coinstatsindex=1 # the Chain page's UTXO figures; rebuilds from genesis, which takes hours
|
|
251
|
+
dbcache=4096 # or what the machine can spare: the expensive reads are disk-bound
|
|
252
|
+
rpcservertimeout=120 # keeps the node from closing a connection under a slow call
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
| line | what it does for this monitor |
|
|
256
|
+
|---|---|
|
|
257
|
+
| `txindex=1` | **Required** for the explorer's transaction pages. Without it a confirmed transaction cannot be looked up by id. |
|
|
258
|
+
| `coinstatsindex=1` | **Optional.** The Chain page's UTXO figures come from `gettxoutsetinfo muhash`; unindexed, that call walks the whole UTXO set (41 s measured), so on a node that reports no synced `coinstatsindex` the monitor does not ask for them at all, leaves the figures blank and flags `utxo-unindexed`. The index **rebuilds from genesis** and takes hours -- until it finishes those figures stay unavailable and the rebuild competes with everything else for the disk. |
|
|
259
|
+
| `dbcache=4096` | Measured 2026-09-13 on one Core 31.1.0 node that shipped with 450 MB: raised to 4096 together with the RPC settings here, the slowest call went from 4.0-4.5 s to 488-565 ms and the monitor's lane stopped timing out. Which line deserved the credit was not isolated, so they are recommended together. |
|
|
260
|
+
| `rpcservertimeout=120` | The monitor's own ceilings are 90 s ordinary / 300 s heavy, so this only matters on a heavily loaded node. |
|
|
261
|
+
| `rpcthreads`, `rpcworkqueue` | **Not for us:** this monitor issues one RPC at a time, so extra node threads do not speed it up. They matter where other software (Electrs, LND) shares the same bitcoind. |
|
|
262
|
+
| `rest=1` | **Nothing.** This monitor makes no REST calls; it is JSON-RPC only. |
|
|
263
|
+
|
|
264
|
+
Restart the node after changing these: `bitcoin.conf` is read at start-up. `peerinfo-partial` is
|
|
265
|
+
normal on Core -- bytes from peers that have since disconnected remain in `getnettotals` but
|
|
266
|
+
leave no per-peer row, so the two do not sum. **Log parsing does not support Core** (see below);
|
|
267
|
+
leave the log source off, nothing in the UI depends on it.
|
|
268
|
+
|
|
269
|
+
**Several nodes** — add more entries to `nodes`; a node picker appears in the header and
|
|
270
|
+
every chart, table and stream is per node.
|
|
271
|
+
|
|
272
|
+
**The node's log.** The monitor works from RPC alone, and that is the supported mode.
|
|
273
|
+
**Log parsing does not currently support Bitcoin Core**: the parsers were written against an
|
|
274
|
+
experimental node with a different log grammar, and fed real Core `debug.log` lines they
|
|
275
|
+
extract no figures and misdate the entries (measured 2026-09-13). Leave `log.enabled` off --
|
|
276
|
+
it is off by default -- and ignore `logFile`. Nothing is lost on Core: `getnettotals` and per-peer
|
|
277
|
+
byte counts are served over RPC (verified 2026-09-13). The Node & RPC page
|
|
278
|
+
lists exactly which figures each source provides.
|
|
279
|
+
|
|
280
|
+
## 5. First run
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
npm run check # every configured node: RPC, credentials, txindex, getblock 3, mempool, block files, index -- every call timed
|
|
284
|
+
npm start
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Watch the start-up lines. You should see the addresses it listens on, a line per node,
|
|
288
|
+
and — because accounts are off by default — a warning that names who can read the monitor. If
|
|
289
|
+
the node entry names an `addressIndex` directory with no index in it, `address index: building
|
|
290
|
+
… with N workers -- the Overview shows the progress` follows, and the build runs on in the
|
|
291
|
+
background (see [Building the address index](#building-the-address-index)). Then open
|
|
292
|
+
<http://127.0.0.1:21000>.
|
|
293
|
+
|
|
294
|
+
Check it from the shell:
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
curl -s http://127.0.0.1:21000/api/health
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
If a node shows as offline, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md#a-node-shows-offline).
|
|
301
|
+
|
|
302
|
+
## 6. Run it as a service
|
|
303
|
+
|
|
304
|
+
> **macOS:** this section is Linux/systemd. On a Mac, either leave `npm start` running in a
|
|
305
|
+
> terminal, or wrap it in a `launchd` plist -- there is no other platform-specific step, and the
|
|
306
|
+
> configuration in section 4 is identical.
|
|
307
|
+
|
|
308
|
+
1. **Create an account for it** that can read the node's cookie and its `blocks/` directory
|
|
309
|
+
(the index is built from the block files), and that can write the index directory. Usually
|
|
310
|
+
that means adding it to the node's group:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
sudo useradd --system --home /opt/blockyard --shell /usr/sbin/nologin blockyard
|
|
314
|
+
sudo usermod -aG <node-group> blockyard
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
2. **Put the code somewhere stable**, for example `/opt/blockyard`, owned by that account:
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
sudo git clone https://github.com/BobClawblaw/blockyard.git /opt/blockyard
|
|
321
|
+
sudo cp config/local.json /opt/blockyard/config/ # the file from step 4
|
|
322
|
+
sudo chown -R blockyard:blockyard /opt/blockyard
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
3. **Install the unit** shipped in `systemd/blockyard.service` and edit it:
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
sudo cp /opt/blockyard/systemd/blockyard.service /etc/systemd/system/
|
|
329
|
+
sudo systemctl edit --full blockyard
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Set `User=` / `Group=` to the account from step 1, `WorkingDirectory=` to where the code
|
|
333
|
+
lives, and replace the `Environment=` lines that name paths with your own (or delete
|
|
334
|
+
them and keep everything in `config/local.json`). Point `ExecStart` at an absolute Node
|
|
335
|
+
22 binary — `/usr/bin/env node` can resolve to an older system Node under systemd:
|
|
336
|
+
|
|
337
|
+
```ini
|
|
338
|
+
ExecStart=/usr/local/bin/node server/main.js
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
4. **Start it:**
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
sudo systemctl daemon-reload
|
|
345
|
+
sudo systemctl enable --now blockyard
|
|
346
|
+
journalctl -u blockyard -f
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
The unit restarts the monitor if it ever exits and gives it time to save its history on
|
|
350
|
+
shutdown. Stop and start with `systemctl stop|start blockyard`.
|
|
351
|
+
|
|
352
|
+
## 7. Decide who can reach it
|
|
353
|
+
|
|
354
|
+
Where the monitor listens is a security decision. Set `server.host` in `config/local.json`
|
|
355
|
+
(a string or a list) or `BLOCKYARD_BIND`:
|
|
356
|
+
|
|
357
|
+
| bind | who can connect | typical use |
|
|
358
|
+
|---|---|---|
|
|
359
|
+
| `"127.0.0.1"` | this machine only | reach it with an SSH tunnel: `ssh -L 21000:127.0.0.1:21000 you@host` |
|
|
360
|
+
| `"192.0.2.10"` (a LAN address) | anything that can route to that address | a home or office LAN |
|
|
361
|
+
| `["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"` (the default) | every interface | behind a firewall you control |
|
|
363
|
+
|
|
364
|
+
Notes:
|
|
365
|
+
|
|
366
|
+
- Binding a single LAN address means `127.0.0.1` on the host itself stops answering — test
|
|
367
|
+
with the address you bound.
|
|
368
|
+
- An address the machine does not have at start-up is skipped with a warning; start-up fails
|
|
369
|
+
only if none of the listed addresses exist.
|
|
370
|
+
- A bind chooses a destination address, not an incoming interface. If "LAN only" must hold
|
|
371
|
+
against containers or tunnels on the same host, add a firewall rule, for example:
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
sudo ufw allow from 192.0.2.0/24 to any port 21000 proto tcp
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
- `server.allowCidrs` (or `BLOCKYARD_ALLOW_CIDRS=192.0.2.0/24,2001:db8::/32`) makes the
|
|
378
|
+
monitor itself refuse clients outside those networks, as a second line of defence.
|
|
379
|
+
|
|
380
|
+
## 8. Accounts (optional)
|
|
381
|
+
|
|
382
|
+
By default anyone who can reach the port reads the monitor as a `viewer` — charts, the
|
|
383
|
+
explorer, the event stream, the read-only RPC console. User administration, the audit trail
|
|
384
|
+
and every node write stay closed.
|
|
385
|
+
|
|
386
|
+
To require sign-in, set `"auth": { "enabled": true }` or `BLOCKYARD_AUTH=1` and restart. The
|
|
387
|
+
first start with an empty data directory creates an `admin` account and prints its password
|
|
388
|
+
**once** in the log. Set your own instead with `BLOCKYARD_ADMIN_PASSWORD` for that first start.
|
|
389
|
+
|
|
390
|
+
Manage accounts from the Admin page, or from the shell — for example, to reset the admin
|
|
391
|
+
password:
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
node scripts/manage-users.js passwd admin
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Roles: `viewer` reads; `operator` may also run node actions that you have enabled; `admin`
|
|
398
|
+
also manages users and reads the audit trail. Node actions are off unless you enable them
|
|
399
|
+
explicitly — see [SECURITY.md](SECURITY.md#node-writes).
|
|
400
|
+
|
|
401
|
+
## 9. HTTPS (optional)
|
|
402
|
+
|
|
403
|
+
Name a certificate and key and every listener serves HTTPS:
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
BLOCKYARD_TLS_CERT=/etc/blockyard/cert.pem BLOCKYARD_TLS_KEY=/etc/blockyard/key.pem npm start
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
or in `config/local.json`: `"server": { "tls": { "cert": "...", "key": "..." } }`.
|
|
410
|
+
|
|
411
|
+
A self-signed certificate is fine on a LAN (expect one browser warning per address):
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
sudo mkdir -p /etc/blockyard
|
|
415
|
+
sudo openssl req -x509 -newkey rsa:3072 -nodes -days 825 \
|
|
416
|
+
-keyout /etc/blockyard/key.pem -out /etc/blockyard/cert.pem \
|
|
417
|
+
-subj "/CN=blockyard" -addext "subjectAltName=IP:192.0.2.10,DNS:blockyard.lan.example"
|
|
418
|
+
sudo chown blockyard:blockyard /etc/blockyard/*.pem && sudo chmod 600 /etc/blockyard/key.pem
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
With TLS on, the session cookie is marked `Secure` and a short HSTS header is sent. A
|
|
422
|
+
half-configured pair (only a cert, or only a key) and an expired certificate stop the start-up
|
|
423
|
+
rather than silently serving plain HTTP.
|
|
424
|
+
|
|
425
|
+
## 10. Behind a reverse proxy (optional)
|
|
426
|
+
|
|
427
|
+
If you already run nginx, Caddy or similar, bind the monitor to `127.0.0.1` and let the proxy
|
|
428
|
+
terminate TLS. The live stream (`/api/stream`) is Server-Sent Events, so the proxy must not
|
|
429
|
+
buffer it. An nginx example:
|
|
430
|
+
|
|
431
|
+
```nginx
|
|
432
|
+
server {
|
|
433
|
+
listen 443 ssl;
|
|
434
|
+
server_name monitor.example.org;
|
|
435
|
+
# ssl_certificate / ssl_certificate_key ...
|
|
436
|
+
|
|
437
|
+
location / {
|
|
438
|
+
proxy_pass http://127.0.0.1:21000;
|
|
439
|
+
proxy_set_header Host $host;
|
|
440
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
441
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
442
|
+
}
|
|
443
|
+
location /api/stream {
|
|
444
|
+
proxy_pass http://127.0.0.1:21000;
|
|
445
|
+
proxy_http_version 1.1;
|
|
446
|
+
proxy_set_header Connection "";
|
|
447
|
+
proxy_buffering off;
|
|
448
|
+
proxy_cache off;
|
|
449
|
+
proxy_read_timeout 1h;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Then set `"server": { "trustProxy": true }` so the monitor uses the forwarded client
|
|
455
|
+
address for rate limits and the CIDR gate, and `BLOCKYARD_SECURE_COOKIE=1` so the session
|
|
456
|
+
cookie is marked `Secure` behind the proxy's TLS.
|
|
457
|
+
|
|
458
|
+
## 11. Updating
|
|
459
|
+
|
|
460
|
+
```bash
|
|
461
|
+
cd /opt/blockyard
|
|
462
|
+
sudo -u blockyard git pull
|
|
463
|
+
sudo systemctl restart blockyard
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
Your `config/local.json` and `data/` directory are untouched by updates. Read
|
|
467
|
+
[CHANGELOG.md](../CHANGELOG.md) for anything that needs your attention. The browser picks
|
|
468
|
+
up new front-end files on the next page load; the header shows a notice when the page you
|
|
469
|
+
have open is older than the server.
|
|
470
|
+
|
|
471
|
+
## 12. Uninstalling
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
sudo systemctl disable --now blockyard
|
|
475
|
+
sudo rm /etc/systemd/system/blockyard.service && sudo systemctl daemon-reload
|
|
476
|
+
sudo rm -rf /opt/blockyard # includes data/: history, accounts and the audit trail
|
|
477
|
+
sudo userdel blockyard
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
## Checklist
|
|
481
|
+
|
|
482
|
+
- [ ] `node -v` prints v22 or later for the account the service runs as
|
|
483
|
+
- [ ] `config/local.json` names your node's RPC URL and a readable cookie (or user/password)
|
|
484
|
+
- [ ] `npm run check` passes: Core 25.0+, `txindex` synced, the block files readable, no pruning
|
|
485
|
+
- [ ] the index directory has ~125 GB free and is writable by the service account
|
|
486
|
+
- [ ] 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 the network is not trusted
|
|
490
|
+
- [ ] `BLOCKYARD_MARKETS=0` if the machine must make no outbound connections
|