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.
Files changed (107) hide show
  1. package/CHANGELOG.md +679 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +4 -0
  4. package/README.md +172 -4
  5. package/SECURITY.md +38 -0
  6. package/bin/blockyard.js +40 -0
  7. package/config/pool-map.json +2620 -0
  8. package/docs/API.md +1575 -0
  9. package/docs/ARCHITECTURE.md +1307 -0
  10. package/docs/AUTO-UPDATE.md +269 -0
  11. package/docs/CONFIGURATION.md +840 -0
  12. package/docs/DEFECTS.md +813 -0
  13. package/docs/EFFECTS-AGENTS.md +448 -0
  14. package/docs/GETTING-STARTED.md +202 -0
  15. package/docs/INSTALL.md +490 -0
  16. package/docs/MEASUREMENTS.md +1254 -0
  17. package/docs/PRIVATE-LEADERBOARD.md +230 -0
  18. package/docs/RULES.md +681 -0
  19. package/docs/SECURITY-AUDIT-2026-09-14.md +177 -0
  20. package/docs/SECURITY-AUDIT.md +258 -0
  21. package/docs/SECURITY.md +195 -0
  22. package/docs/STATE-2026-09-09.md +200 -0
  23. package/docs/TROUBLESHOOTING.md +298 -0
  24. package/docs/USER-GUIDE.md +1022 -0
  25. package/package.json +53 -5
  26. package/public/404.html +9 -0
  27. package/public/css/app.css +1785 -0
  28. package/public/index.html +893 -0
  29. package/public/js/about.js +112 -0
  30. package/public/js/agents.js +964 -0
  31. package/public/js/app.js +1312 -0
  32. package/public/js/arkanoid.js +806 -0
  33. package/public/js/blockanoid.js +347 -0
  34. package/public/js/blockout.js +347 -0
  35. package/public/js/blockpack.js +428 -0
  36. package/public/js/blockscene3d.js +2678 -0
  37. package/public/js/breakout.js +224 -0
  38. package/public/js/charts.js +635 -0
  39. package/public/js/depthchart.js +311 -0
  40. package/public/js/details3d.js +2957 -0
  41. package/public/js/explorer.js +405 -0
  42. package/public/js/feepalette.js +149 -0
  43. package/public/js/fmt.js +162 -0
  44. package/public/js/goggles.js +886 -0
  45. package/public/js/kiosk.js +41 -0
  46. package/public/js/login.js +83 -0
  47. package/public/js/markets.js +357 -0
  48. package/public/js/mining.js +1138 -0
  49. package/public/js/panels.js +966 -0
  50. package/public/js/pricechart.js +188 -0
  51. package/public/js/settings.js +1014 -0
  52. package/public/js/tetris.js +226 -0
  53. package/public/js/tetrust.js +356 -0
  54. package/public/js/tetsound.js +175 -0
  55. package/public/login.html +33 -0
  56. package/scripts/blockfile-measure.js +156 -0
  57. package/scripts/browser-check.mjs +286 -0
  58. package/scripts/check.js +173 -0
  59. package/scripts/decode-check.js +81 -0
  60. package/scripts/doc-counts.js +109 -0
  61. package/scripts/donate-qr.py +20 -0
  62. package/scripts/fake-node.js +534 -0
  63. package/scripts/index-bench.js +216 -0
  64. package/scripts/index-benchmark.js +117 -0
  65. package/scripts/index-build.js +40 -0
  66. package/scripts/live-render-check.mjs +89 -0
  67. package/scripts/manage-users.js +132 -0
  68. package/scripts/motion-check.mjs +138 -0
  69. package/scripts/pool-map.js +157 -0
  70. package/scripts/setup.js +410 -0
  71. package/scripts/shots.mjs +272 -0
  72. package/scripts/smoke.sh +327 -0
  73. package/scripts/ui.js +174 -0
  74. package/server/auth/sessions.js +221 -0
  75. package/server/auth/users.js +243 -0
  76. package/server/chain/blockfile.js +234 -0
  77. package/server/chain/index/build.js +193 -0
  78. package/server/chain/index/heights.js +36 -0
  79. package/server/chain/index/live.js +276 -0
  80. package/server/chain/index/rows.js +145 -0
  81. package/server/chain/index/store.js +154 -0
  82. package/server/chain/index/worker.js +109 -0
  83. package/server/chain/tx.js +310 -0
  84. package/server/collect/gbt.js +229 -0
  85. package/server/collect/logparse.js +765 -0
  86. package/server/collect/logtail.js +189 -0
  87. package/server/collect/markets.js +333 -0
  88. package/server/collect/mining.js +333 -0
  89. package/server/collect/monitor.js +2516 -0
  90. package/server/collect/nextblock.js +275 -0
  91. package/server/collect/sync.js +386 -0
  92. package/server/config.js +620 -0
  93. package/server/http/api.js +1275 -0
  94. package/server/http/explorer.js +418 -0
  95. package/server/http/server.js +412 -0
  96. package/server/http/sse.js +176 -0
  97. package/server/http/static.js +212 -0
  98. package/server/main.js +628 -0
  99. package/server/netinfo.js +253 -0
  100. package/server/rpc/allowlist.js +130 -0
  101. package/server/rpc/client.js +414 -0
  102. package/server/store/audit.js +148 -0
  103. package/server/store/history.js +220 -0
  104. package/server/store/ledger.js +290 -0
  105. package/server/store/ring.js +173 -0
  106. package/server/util/fmt.js +29 -0
  107. package/systemd/blockyard.service +100 -0
@@ -0,0 +1,298 @@
1
+ # Troubleshooting
2
+
3
+ The monitor tries to explain itself: the start-up log, the header badges and the **Node &
4
+ RPC** page usually name the problem. This page collects the cases people actually hit.
5
+
6
+ - [The service does not start](#the-service-does-not-start)
7
+ - [A node shows offline](#a-node-shows-offline)
8
+ - [The page does not load from another machine](#the-page-does-not-load-from-another-machine)
9
+ - [The header says "stale" or "reconnecting"](#the-header-says-stale-or-reconnecting)
10
+ - [The header says STALLED](#the-header-says-stalled)
11
+ - [The node's RPC is slow](#the-nodes-rpc-is-slow)
12
+ - [The block-space board is empty, or "no block template yet"](#the-block-space-board-is-empty-or-no-block-template-yet)
13
+ - [The address index build is paused, or slow](#the-address-index-build-is-paused-or-slow)
14
+ - [Markets or Kiosk show no prices](#markets-or-kiosk-show-no-prices)
15
+ - [An exchange shows an error](#an-exchange-shows-an-error)
16
+ - [Explorer: a transaction id is not found](#explorer-a-transaction-id-is-not-found)
17
+ - [Explorer: address pages read "not indexed"](#explorer-address-pages-read-not-indexed)
18
+ - [Explorer: the address page says the index is behind, or has stopped following](#explorer-the-address-page-says-the-index-is-behind-or-has-stopped-following)
19
+ - [Explorer: "spent by" links are missing](#explorer-spent-by-links-are-missing)
20
+ - [Explorer: no dollar figures](#explorer-no-dollar-figures)
21
+ - [The 3D views are slow or blank](#the-3d-views-are-slow-or-blank)
22
+ - [Full screen does not work on the Kiosk](#full-screen-does-not-work-on-the-kiosk)
23
+ - [Sign-in problems](#sign-in-problems)
24
+ - [Tests fail on a fresh clone](#tests-fail-on-a-fresh-clone)
25
+
26
+ ## The service does not start
27
+
28
+ Read the journal first: `journalctl -u blockyard -n 100 --no-pager`.
29
+
30
+ | message or symptom | cause and fix |
31
+ |---|---|
32
+ | a syntax error at start-up | the runtime is older than Node 22. Check `node -v` **as the service account**, and give `ExecStart` an absolute path to a Node 22 binary. |
33
+ | `EADDRINUSE` | something else holds the port. Change `server.port` / `BLOCKYARD_PORT`, or stop the other process (`ss -ltnp | grep 21000`). |
34
+ | none of the configured addresses exist | the bind names addresses this machine does not have (for example after a DHCP change). The log prints the addresses it does have; fix `server.host`. |
35
+ | `Invalid configuration` followed by a list | each line names the setting and why — for example a certificate without a key, an expired certificate, a malformed CIDR, or node writes enabled without accounts. |
36
+ | `config: cannot parse …/local.json` | the JSON has a syntax error (a trailing comma is the usual one). |
37
+
38
+ ## A node shows offline
39
+
40
+ The Node & RPC page shows the last error. Common causes:
41
+
42
+ - **`ECONNREFUSED`** — nothing is listening at `rpcUrl`. Check the node is running and which
43
+ RPC port it uses (`rpcport` in its configuration); the node may also bind RPC a little while
44
+ after its service reports "started".
45
+ - **`401` / authentication failed** — the cookie could not be read or is out of date. The
46
+ cookie is rewritten on every node restart and removed when the node stops. Make sure the
47
+ service account can read `<datadir>/<chain>/.cookie` (usually by being in the node's group),
48
+ or configure `rpcUser` / `rpcPassword`.
49
+ - **"datadir does not exist"** at start-up — the node entry points at a directory that is not
50
+ there; the node is skipped rather than shown as permanently offline. Fix `datadir` or set
51
+ `cookieFile` directly.
52
+ - **Timeouts** right after the node starts — some node builds block RPC for a while during
53
+ start-up. The monitor keeps retrying; wait a minute.
54
+
55
+ ## The page does not load from another machine
56
+
57
+ 1. From the other machine, check how it reaches the server: `ip route get <address>`.
58
+ 2. Make sure that address is one the monitor binds — see the start-up log. A LAN-only bind is
59
+ not reachable over a VPN unless the VPN address is also listed in `server.host`.
60
+ 3. Check firewalls on the server (`ufw status`, `nft list ruleset`) for the port.
61
+ 4. If `server.allowCidrs` is set, the client's network must be in it.
62
+ 5. Testing from the server itself proves little: with a single LAN address bound, `127.0.0.1`
63
+ does not answer, and a firewall usually allows loopback.
64
+
65
+ ## The header says "stale" or "reconnecting"
66
+
67
+ - **reconnecting** — the live stream (Server-Sent Events) dropped. Behind a reverse proxy the
68
+ usual cause is response buffering or a short read timeout on `/api/stream`; see the proxy
69
+ example in [INSTALL.md](INSTALL.md#10-behind-a-reverse-proxy-optional).
70
+ - **stale** — no fresh data has arrived for a while. Charts keep the last picture with a
71
+ "stale" label rather than going blank; the Node & RPC page shows which poll is failing.
72
+
73
+ ## The header says STALLED
74
+
75
+ A long gap between blocks is not a stall: the network finds no block for 40 minutes about once
76
+ in fifty, and two healthy nodes at the same height once sat through 42 minutes of it with the
77
+ header red. Since 2026-09-14 the sync state is **stalled** only when the node's **connected
78
+ peers report a higher tip than the node holds** (`getpeerinfo` `synced_headers`) — it is behind
79
+ the network, not waiting for it. When the peers agree on the node's tip, a long gap is shown as
80
+ *synced* with a caveat naming the gap. When no peer height is known at all (no peers, or none
81
+ that report one), the node is called stalled only after **two hours** without a block, because a
82
+ long gap and a node cut off from its peers cannot be told apart sooner. The sync panel's caveats
83
+ say which of the three cases it is.
84
+
85
+ If it really is stalled: check the node's peer count and its own log; a node behind a firewall
86
+ that has lost its connections looks exactly like this.
87
+
88
+ ## The node's RPC is slow
89
+
90
+ The Node & RPC page shows RPC latency and the queue. When the node is slow the monitor
91
+ stretches its polling automatically and skips heavy reads, so a busy node (for example during
92
+ initial sync) shows fewer updates rather than being slowed further. Slow means an average above
93
+ `rpc.slowLatencyMs` (5 s by default): the `rpc-slow` flag appears on the Overview, and a running
94
+ address index build pauses until the node recovers (see
95
+ [below](#the-address-index-build-is-paused-or-slow)).
96
+
97
+ To time the node **alone**, with no monitor in the way: `npm run check` runs the install-time
98
+ checks against every configured node and prints how long each call took — `getblockchaininfo`,
99
+ `getblock <tip> 3` and a verbose `getrawmempool` among them. A `getblock 3` over 5 s or a mempool
100
+ read over 10 s is marked slow there, with what it will mean for the pages.
101
+
102
+ The monitor runs **one** request at a time, always. `rpc.maxInFlight` exists in the config and
103
+ is reported by `/api/config`, but the lane is serialised by construction and does not read it --
104
+ measured 2026-09-13 at 1, 4 and 8: four 200 ms jobs took ~807 ms with peak concurrency 1 in every
105
+ case. Treat it as documentation of intent, not a tuning knob. (The address index build has a
106
+ second connection of its own for its few cheap calls; it is paced by the first lane's telemetry
107
+ and adds nothing while the node is slow.)
108
+
109
+ What costs time on a mainnet node is the expensive reads, and how much depends on how the node is
110
+ configured. The sharpest example used to be the block template -- since 2026-09-13 the monitor
111
+ assembles that from the mempool and never calls `getblocktemplate`, so the numbers below are kept
112
+ as the clearest illustration of what node tuning is worth, not as a call this software still
113
+ makes. Measured on a Core 31.1.0 node on 2026-09-13, **before** its RPC settings were
114
+ tuned: `getblockchaininfo` 95-110 ms, `getmempoolinfo` ~100 ms, but `getblocktemplate`
115
+ **4.0-4.5 s**, five times in a row with no warming -- while the same call on a local Core node
116
+ answered in **51 ms**. Two concurrent templates contended rather than overlapped there (6.9 s and
117
+ 8.8 s against 4.5 s alone), so more concurrency would not have helped that call.
118
+
119
+ **After** applying `dbcache=4096` plus the RPC settings in [INSTALL](INSTALL.md), the same node
120
+ answered `getblocktemplate` in **488-565 ms** and the monitor's lane stopped timing out entirely,
121
+ with average latency sampling between ~180 ms and a few seconds. If your node shows
122
+ `rpc-slow`, check those settings before concluding the node is simply slow -- and note that a
123
+ rebuilding index (`coinstatsindex` takes hours from genesis) competes for the same disk and will
124
+ keep latency up until it finishes.
125
+
126
+ ## The block-space board is empty, or "no block template yet"
127
+
128
+ The board and the block being built are **assembled from the node's verbose mempool**
129
+ (`getrawmempool true`), which the monitor reads every 20 s; the monitor never calls
130
+ `getblocktemplate`. The board is empty when that read has not succeeded yet:
131
+
132
+ - **Right after start-up** — the first verbose read comes a few seconds in; the board lands a
133
+ little after the Overview fills.
134
+ - **The node's RPC is slow** — a verbose mempool read on a large mempool is the monitor's
135
+ heaviest regular call, and on a slow node it is the one that takes tens of seconds. While the
136
+ lane's average latency is over the slow threshold the heavy tiers are skipped
137
+ (`heavy-tiers-skipped` on the Overview), and a read that is superseded before it finishes is
138
+ **dropped as stale** rather than queued (`getrawmempool verbose failed` in the events feed) — so
139
+ the board stays as it was, or empty. `npm run check` times that same read against the node with
140
+ nothing else in the way; if it is slow there too, the node is the bottleneck: see
141
+ [above](#the-nodes-rpc-is-slow) and the `dbcache` / `rpcservertimeout` lines in
142
+ [INSTALL](INSTALL.md#bitcoinconf-settings-worth-having). An address index build on the same disk
143
+ pauses itself while this lasts.
144
+ - **Initial block download** — the card says so; there is no chain to build a block on yet.
145
+ - **`BLOCKYARD_MINING_TEMPLATE=0`** — the block being built is disabled by configuration.
146
+
147
+ ## The address index build is paused, or slow
148
+
149
+ The Overview's "What this panel cannot tell you" box shows the build's phase, files done, rows
150
+ so far and an ETA; the address page repeats it. Things it says, and what they mean:
151
+
152
+ - **paused while the node's RPC is slow** — by design. The build's workers read the block files
153
+ the node is also reading, so before each file the build checks the monitor's RPC telemetry:
154
+ while the node is failing or answering slower on average than `rpc.slowLatencyMs` (5 s by
155
+ default) it **holds**, rechecking every 10 s; while merely slow it eases off between files. The
156
+ log says `address index build: paused while the node's RPC is answering in N s` and `resumed`.
157
+ A build that is paused most of the time means the node cannot keep up with the monitor and the
158
+ build together on that disk: fewer workers, or a node tuned as in
159
+ [INSTALL](INSTALL.md#bitcoinconf-settings-worth-having), or let it run overnight.
160
+ - **The ETA is wrong at first** — it is computed from the files done so far in the current phase
161
+ and settles after the first few; files are not all the same size.
162
+ - **Hours, not minutes** — expect **a few hours**: 29 min 45 s is 16 workers on NVMe, and four
163
+ workers (the installer's default) are roughly four times slower; **spinning disks** are slower still whatever the
164
+ number, and there one worker is the fast setting, because parallel readers only seek against
165
+ each other and against the node. Set `addressIndexWorkers` on the node entry in
166
+ `config/local.json` (the installer writes the number you gave it) and restart: there is no
167
+ resume, so the build starts over from the first file.
168
+ - **It started over** — stopping BlockYard stops the build, and the next start begins it again
169
+ from scratch. Leave it running until the notification says it is done.
170
+ - **the address index build failed** — the reason is in the events feed and the log; fix it and
171
+ restart (the server builds again), or run `node scripts/index-build.js --out <dir>` by hand.
172
+ A pruned node, unreadable block files and a full disk are the usual causes.
173
+
174
+ ## Markets or Kiosk show no prices
175
+
176
+ - **"market data is off"** — `BLOCKYARD_MARKETS=0` or `markets.enabled: false` is set.
177
+ - **"asking the exchanges…" for a long time** — the server cannot reach the exchanges. Test
178
+ from the server: `curl -sI https://api.exchange.coinbase.com/products/BTC-USD/ticker`. Check
179
+ outbound firewall rules and DNS.
180
+ - Market data is only fetched while the Markets, Kiosk or Overview tab is open; the first prices take a
181
+ few seconds, the order-book depth up to half a minute.
182
+
183
+ ## An exchange shows an error
184
+
185
+ The exchange table shows each exchange's last error next to its row.
186
+
187
+ | error | meaning |
188
+ |---|---|
189
+ | `HTTP 451` | the exchange refuses requests from your region. |
190
+ | `HTTP 429` | the exchange is rate-limiting this address; the monitor retries on its next cycle. |
191
+ | `fetch failed` | a network error reaching that exchange (DNS, firewall, or no route over IPv6 while IPv4 works). The monitor already allows slow connection set-up; persistent failures are usually a firewall. |
192
+
193
+ A failing exchange is left out of the median and the spread; the others keep working.
194
+
195
+ ## Explorer: a transaction id is not found
196
+
197
+ The explorer asks the node for `getrawtransaction <txid> 2` with no block hash. A node without a
198
+ transaction index can only answer that for transactions still in its **mempool**, so a confirmed
199
+ transaction looks missing even though the node is healthy and fully synced. Block pages are
200
+ unaffected — they pass the block hash, so the node can find the transaction without an index.
201
+
202
+ Set `txindex=1` in `bitcoin.conf` and restart the node. Adding it to a node that has been running
203
+ without one triggers a one-off reindex; `getindexinfo` reports progress and says `"synced": true`
204
+ when it is done:
205
+
206
+ ```bash
207
+ bitcoin-cli getindexinfo
208
+ ```
209
+
210
+ ## Explorer: address pages read "not indexed"
211
+
212
+ **Bitcoin Core has no address index at any setting, and this is not something you have
213
+ misconfigured.** `getaddressbalance` and `getaddresstxids` are insight-style extensions that only
214
+ forks carry; stock Core answers `Method not found` (measured 2026-09-13 on two Core nodes). There is no node option to enable.
215
+
216
+ BlockYard builds its own index from the node's block files instead — about 30 minutes on 16
217
+ workers and 124 GB of disk for the whole chain — and the server keeps it current as blocks
218
+ arrive. It builds it **by itself, in the background**, when it starts with an `addressIndex`
219
+ directory that holds no index (the installer's default); while that runs the address page says
220
+ *the address index is being built* with the progress, and fills in when it is done. So *not
221
+ indexed* with no build in progress means one of:
222
+
223
+ - no `addressIndex` directory on the node entry in `config/local.json` — `npm run setup` writes
224
+ one (`data/index` by default), or add it by hand and restart;
225
+ - `addressIndexBuild: "manual"` on the node entry (the installer's **(l)ater** answer) — run
226
+ `node scripts/index-build.js --out <dir>` and restart, or remove the key and restart;
227
+ - the build failed — the reason is in the events feed and the log (see
228
+ [above](#the-address-index-build-is-paused-or-slow)).
229
+
230
+ [Building the address index](INSTALL.md#building-the-address-index) has the details.
231
+
232
+ Until then the address page confirms the address and its type (`validateaddress` needs no
233
+ index) and marks balance, totals and history as **not indexed**. It does not report a
234
+ transaction count of `0` — nothing counted — and it does not print the node's error where a
235
+ figure belongs.
236
+
237
+ ## Explorer: the address page says the index is behind, or has stopped following
238
+
239
+ **Behind** by a block or two is normal: the follower polls every 30 s and fetches each new block
240
+ with `getblock <hash> 3`; it was measured reaching a new block 16 s after the node. An index
241
+ built a while ago catches up 50 blocks a poll. Behind by many blocks and not closing the gap means
242
+ the follower is failing: usually because `live.log` and `layers/` live **inside the index
243
+ directory**, which must be writable by the service user. A follower that cannot open its index
244
+ is reported in the server log at boot (`address index <dir>: ...`); one whose poll fails keeps
245
+ retrying every 30 s. `npm run check` reports how far behind the index is and whether the
246
+ directory is writable.
247
+
248
+ **Stopped following** means a reorganisation deeper than the blocks the follower still holds in
249
+ its tail (100), which cannot be repaired in place. Stop the server and either delete the index
250
+ directory (the server builds it again on the next start) or run the same `index-build.js`
251
+ command into the same directory — the build empties it first, the old log and layers included —
252
+ and start the server again.
253
+
254
+ ## Explorer: "spent by" links are missing
255
+
256
+ "Spent by" links come from `gettxspendingprevout`, which Bitcoin Core (24.0 and later) answers
257
+ from its **mempool** only: an output spent by a transaction that is still unconfirmed gets a link,
258
+ an output spent in a block does not, and there is no node index to turn on for that. Looking up
259
+ an arbitrary historical transaction by id is a different matter — that needs `txindex` (see
260
+ [above](#explorer-a-transaction-id-is-not-found)).
261
+
262
+ ## Explorer: no dollar figures
263
+
264
+ Dollar amounts appear only when a spot price is available within a moment of loading the page:
265
+ from a fresh Markets feed, or from one quick ticker request. With market data off, or with the
266
+ exchanges unreachable, pages show BTC figures only — never a guessed price.
267
+
268
+ ## The 3D views are slow or blank
269
+
270
+ - **Slow** — the dense viewer mode (**Detailed**) draws thousands of tiles; on an old
271
+ or GPU-less machine switch the Block space viewer to **Simple** in its control bar.
272
+ The Markets board keeps animating while visible (for its star field); switching to another
273
+ tab stops it.
274
+ - **No motion** — the browser or operating system has "reduce motion" on; the views then draw
275
+ without animation, by design.
276
+ - **Blank** — reload the page. If it stays blank, open the browser console and look for errors;
277
+ a stale cached front-end after an update is the usual cause, and the header shows a notice
278
+ when the page is older than the server.
279
+
280
+ ## Full screen does not work on the Kiosk
281
+
282
+ Browsers allow full screen only after a click on the page, and some embedded or kiosk-mode
283
+ browsers refuse it entirely. Use the browser's own full-screen key (F11) instead, or start the
284
+ browser in kiosk mode pointed at `http://<host>:21000/#kiosk`.
285
+
286
+ ## Sign-in problems
287
+
288
+ - **Lost the admin password** — `node scripts/manage-users.js passwd admin` on the server.
289
+ - **"too many attempts"** — the lockout lasts 10 minutes per username and per address.
290
+ - **Sign-in does not stick** — over plain HTTP, make sure `BLOCKYARD_SECURE_COOKIE` is not set
291
+ (a `Secure` cookie is never sent over HTTP). Behind a TLS proxy, set it.
292
+
293
+ ## Tests fail on a fresh clone
294
+
295
+ - Use Node 22 or later (`node -v`).
296
+ - The suite is hermetic — it does not read `config/local.json` and needs no node — but a few
297
+ tests boot servers on local ports; make sure nothing else holds them.
298
+ - Timing-sensitive tests can fail on a heavily loaded machine; run the suite again.