argus/OPERATIONS-electrum.md
Local Dev a45320db7d Initial commit — Silent Mode baseline (2026-07-29)
Snapshot of the decentralized-web stack at the point of the resolver+Theseus
rebuild deploy. Includes:

- Argus (BNS engine + resolver daemon + Sia gateway)
- AriadneResolver (Windows Inno installer bundle + Android APK sources +
  Firefox extension)
- TheseusNavigator (Electron browser)
- site/ (silentmode.st content, deployed to Sia at bns/silentmode/)
- design docs, roadmap, protocol spec

Secrets excluded via .gitignore: Argus/sia-s3.json, Argus/wallets.json,
Argus/ca/*.key,*.crt. Build outputs, node_modules, and bundled runtimes
also excluded.

Shipped hashes on dl.silentmode.st at this commit:
  AriadneResolver-Setup-0.1.0.exe    5bcb216eef31ea28ed767e4134ab74bd5ac69dfbd365fd249e9e6938e55c986a
  TheseusNavigator-Setup-0.0.1.exe   7c735e88bad2da3347145adba3016c8f626a18b8422289c8c6ba471972e2952b
  TheseusNavigator-0.0.1-portable.exe 008fd84445babeabb401b2bca40ea9466b24b0e6d6c85104da7640c5c5c84521
  ariadne-v0.2.apk                   635c8f04d44ef855a8390b9eeddb8cd2d50622e81cc4e5004e8daffc1bb0425c
2026-07-29 13:54:34 +02:00

122 lines
4.7 KiB
Markdown

# 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`.