argus/OPERATIONS-electrum.md

123 lines
4.7 KiB
Markdown
Raw Normal View History

# Running your own Fulcrum — and why the resolver wants you to
Referenced from `src/registrar-config.js` (`REGISTRAR.selfHosted`).
## The problem this solves
Public electrum servers listen on **non-standard ports**: 50004 and 62002. On
2026-07-27 all three configured chipnet servers went TCP-closed from one machine
at the same instant, while `:443` stayed perfectly reachable. That is the
signature of a firewall or VPN policy, not an outage — and it is the normal
condition on corporate networks, on some mobile carriers, and behind many
consumer VPNs.
The consequence is worse than it sounds. Every server in the list shares the same
two ports, so **one rule takes down the entire resolver**: no availability
search, no registration, no record updates, for that user, everywhere the
software runs — website, extension, Theseus, Ariadne.
A server on 443 is indistinguishable from ordinary HTTPS traffic and is
essentially never blocked. **One server on 443 is worth more than three more on
50004.**
## The second reason: not trusting one server
A single electrum server is a trusted third party. It cannot forge a
registration — that needs a real CashTokens genesis whose commitment matches the
name — but it can **omit** one. Because the protocol rule is *"first valid REG
wins"*, a hidden REG makes a taken name look available: the buyer pays, mints a
duplicate certificate, and every other resolver rejects it.
`ElectrumPool.unionHistory` defends against exactly this by taking the **union**
of beacon history across every server that answers, and reporting which servers
were short. Union rather than quorum, because omission is the attack: a server
showing you *more* history is giving you more truth, and two stale servers must
not be able to outvote one honest one.
**So running your own server is not a centralisation trade.** Your server is the
fast, unblockable path; the public ones stay in the list as cross-checks. The
union means your own server cannot quietly hide a registration from you either.
## Configuration
```js
// src/registrar-config.js
selfHosted: "wss://bns.example.st:443",
```
That is all. The pool connects in parallel, prefers earlier entries, fails over
automatically, and unions the history regardless of order.
## Running Fulcrum
Fulcrum indexes a full node; you need both.
1. **Bitcoin Cash Node** with the indexes Fulcrum needs:
```
txindex=1
server=1
rpcuser=…
rpcpassword=…
chipnet=1 # or omit for mainnet
```
2. **Fulcrum**, pointed at it. The part that matters here is the **WSS port on
443**:
```
bitcoind = 127.0.0.1:8332
rpcuser = …
rpcpassword = …
ssl = 0.0.0.0:50002
wss = 0.0.0.0:443 # <- the one that survives restrictive networks
cert = /etc/letsencrypt/live/bns.example.st/fullchain.pem
key = /etc/letsencrypt/live/bns.example.st/privkey.pem
```
3. **A real certificate.** Browsers will not open a `wss://` connection with a
self-signed cert, and the failure is opaque — it surfaces as a generic
WebSocket error with no indication that the certificate is the cause. Use
Let's Encrypt.
4. **Binding 443 needs privilege.** Either `setcap
'cap_net_bind_service=+ep'` on the Fulcrum binary, or terminate TLS at nginx
and proxy to Fulcrum's plain WS port. The nginx route is usually easier
because it shares 443 with the rest of the site:
```nginx
location /electrum {
proxy_pass http://127.0.0.1:50003;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s; # electrum connections are long-lived
}
```
Then `selfHosted: "wss://bns.example.st:443/electrum"`.
## Verifying it
```bash
# reachable at all?
curl -sk -o /dev/null -w "%{http_code}\n" https://bns.example.st:443
# does it speak electrum?
cd Argus && node --input-type=module -e "
import WebSocket from 'ws';
import { Electrum } from './src/lib/electrum.js';
const el = await new Electrum('wss://bns.example.st:443', WebSocket).connect();
console.log(await el.call('server.version', ['probe','1.4.2']));
console.log('height:', (await el.call('blockchain.headers.subscribe')).height);
el.close(); process.exit(0);"
```
The second command is the one that matters — a server can answer HTTPS and still
not be a working electrum endpoint.
## Note for the browser
The register page can only reach electrum over **WSS**. It cannot open raw TCP,
so it can never talk to a P2P node directly, and it cannot use a plain-TCP
electrum port. Every entry in the list must be `wss://`. This is also why
SPV-grade *verification* (headers + merkle proofs over electrum) is the right
target for the web client rather than direct-to-node SPV — see
`Decentralized.DNS/DESIGN-trustless-purchase.md`.