igneum-seed-1 (Hetzner cx23, fsn1, 188.245.5.161:26611): built on the VM in 1,530 s, synced to the live devnet (12,204 blocks, same sink as the live node) through a non-mining relay igneumd on the Mac (the live node's addPeer RPC is refused in safe mode); the live node and the Windows PC learned the seed's address by peer exchange and dialled it. seeds.txt written. Hetzner prices corrected to USD (pricing API currency) in the plans, READMEs and scripts; current-generation types per location (cx23 EU, cpx22 sin, cpx21 US) in the cloud-devnet config. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
96 lines
8 KiB
Markdown
96 lines
8 KiB
Markdown
# Seed nodes: plan
|
|
|
|
3 October 2026. Scripts in `infra/seed-nodes/`. The first seed is live.
|
|
|
|
## The first seed
|
|
|
|
| Item | Value |
|
|
|---|---|
|
|
| Name | igneum-seed-1 |
|
|
| Address | `188.245.5.161:26611` (Hetzner primary IPv4, auto-delete off, so a rebuilt server keeps it) |
|
|
| Provider, location, type | Hetzner Cloud, Falkenstein (fsn1), cx23 (2 Intel vCPU, 4 GB, 40 GB), Debian 12 |
|
|
| Cost | USD 6.49 per month net, USD 7.79 gross, USD 0.0104 per hour, 20 TB traffic included (Hetzner API, 3 Oct 2026); plus the primary IPv4, USD 0.60 per month net, 0.72 gross (pricing API). Total about USD 7.09 net, USD 8.51 gross per month. The account bills in USD: the pricing API reports `currency: USD`, VAT 20% |
|
|
| Firewall | inbound tcp 26611 from anywhere, tcp 22 from anywhere (the project lead's instruction; `SSH_SOURCE=me` narrows it to this Mac's IP), icmp; RPC bound to 127.0.0.1 only |
|
|
| Network | the shared devnet (`--devnet`, no suffix), built from `vendor/igneum-node` HEAD d62708a8 plus the uncommitted finality v2 work and `igneum-pow` HEAD, with `--features igneum-pow` |
|
|
| Role | p2p open, no mining, address manager on (serves `RequestAddresses`), `--nodnsseed`, UPnP off, `--externalip` set |
|
|
| How the Mac reaches it | the Mac's live node sits behind NAT (192.168.68.64), so the seed cannot dial in; the Mac dials out. The `addPeer` RPC on the live node is refused ("Method unavailable in safe mode. Run the node with --unsaferpc"), so the working path is a relay: `infra/seed-nodes/addpeer-from-mac.sh relay start`, a second non-mining `igneumd` on the Mac (appdir /tmp/igneum-seed-relay, RPC 127.0.0.1:26680, wRPC 28680, p2p 127.0.0.1:26681) with `--addpeer=192.168.68.64:26611 --addpeer=188.245.5.161:26611`; it syncs from the live node and the seed syncs from it. The live node is untouched. When the live node is next restarted by hand, add `--addpeer=188.245.5.161:26611` to its flags and the relay is no longer needed |
|
|
|
|
`infra/seed-nodes/seeds.txt` holds exactly `188.245.5.161:26611`.
|
|
|
|
Verified 3 Oct 2026, 22:19 to 22:26 UTC: build on the VM 1,530 s (25.5 min, 2 vCPU, 2 jobs, 6 GB swap unused);
|
|
`igneumd/2.1.0` started with the finality v2 parameters, "External address is publicly routable 188.245.5.161:26611";
|
|
the relay on the Mac connected to it at once (protocol 12) and to the live node (protocol 11); the seed completed IBD
|
|
from the relay and reported `isSynced: true` at 22:25:39 UTC with 12,204 blocks and sink `a0a776bb129c...`, the same
|
|
block count and sink the live node reported in the same second. Peer exchange: without any configuration on their
|
|
side, the live node (`/kaspad:2.1.0/`, protocol 11) and the Windows PC's node (192.168.68.67, `/igneumd:2.1.0/`,
|
|
protocol 11) learned the seed's address from the relay and dialled it themselves; the live node's `getConnectedPeerInfo`
|
|
lists `188.245.5.161:26611` as an outbound peer, and the seed shows three inbound peers from the Mac's public IP.
|
|
`health.sh`: `OK igneum-seed-1 188.245.5.161 p2p=open unit=active rpc=yes synced=True blocks=12204 headers=12204 peers=3`.
|
|
The seed's own known-address table is empty because all three peers are behind NAT with no routable advertised address;
|
|
the first public node that connects will populate it.
|
|
|
|
## How the seed list reaches clients
|
|
|
|
1. Baked into the node. `vendor/igneum-node/consensus/core/src/config/params.rs` holds the per-network seed list as
|
|
`dns_seeders: &'static [&'static str]` on `Params`: `MAINNET_PARAMS` (line 617 on 3 Oct 2026), `TESTNET_PARAMS`
|
|
(670), `SIMNET_PARAMS` (721), `DEVNET_PARAMS` (785), all `&[]` since the rename commit emptied Kaspa's nine mainnet
|
|
and three testnet hostnames. The connection manager resolves each entry with `(seeder, default_p2p_port).to_socket_addrs()`
|
|
(`components/connectionmanager/src/lib.rs`, `dns_seed_single`), so a plain IPv4 literal works as an entry with no
|
|
DNS at all: `dns_seeders: &["188.245.5.161"]` on `DEVNET_PARAMS` is the whole change for the devnet, and the
|
|
testnet list is the same shape with the testnet seeds. The port is the network's default p2p port (devnet 26611;
|
|
the testnet port is still Kaspa's and must be set with the testnet genesis). The consensus engineer owns this edit.
|
|
Two consequences for packages: a client that passes `--nodnsseed` ignores the baked list (`kaspad/src/daemon.rs`
|
|
line 573: `dns_seeders` is emptied when `--nodnsseed` or `--connect` is given), so the Windows node package
|
|
(`proto-cuda/windows-node/start-node.ps1`) and the cloud scripts must drop `--nodnsseed` once the list is baked;
|
|
and the list is consulted only when the node is short of outbound peers, so a node with enough `--addpeer`
|
|
entries never asks a seed.
|
|
2. `SEED_PEERS` override in every package. Each launcher (Windows node, cloud devnet, seed nodes, the observer's
|
|
helper node) reads `SEED_PEERS` (comma-separated `ip:port`) and turns every entry into `--addpeer=<entry>`; the
|
|
baked list is the default when the variable is empty. This is what an operator uses when the baked list is stale
|
|
between releases.
|
|
3. DNS names only as a convenience. `seed1.igneum.network` and so on can point at the same addresses (the domains
|
|
are on Vercel nameservers, so a record each), and `dns_seeders` accepts a hostname too; but the IPs are the source
|
|
of truth because a DNS failure or a registrar problem must not stop bootstrapping, and because the public key of
|
|
nothing is involved: a seed only hands out addresses, it cannot forge blocks.
|
|
|
|
## Public testnet seed set
|
|
|
|
Three to five seeds across two providers and three regions. Proposed:
|
|
|
|
| Seed | Provider | Location | Type | Per month net |
|
|
|---|---|---|---|---|
|
|
| igneum-seed-1 | Hetzner | Falkenstein (EU) | cx23 | USD 6.49 (live) |
|
|
| igneum-seed-2 | Hetzner | Ashburn (US east) | cpx11 (2 GB) or cpx21 (4 GB) | USD 20.49 or 37.49 |
|
|
| igneum-seed-3 | DigitalOcean | Singapore (sgp1) | s-2vcpu-4gb | USD 24 (DO pricing page) |
|
|
| igneum-seed-4 (optional) | DigitalOcean | New York or Frankfurt | s-2vcpu-4gb | USD 24 |
|
|
| igneum-seed-5 (optional) | Hetzner | Helsinki | cx23 | USD 6.49 |
|
|
|
|
Three seeds: about USD 50 per month; five: about USD 80 (approximate, mixed currencies). The US seed is the expensive
|
|
one because Hetzner's current cx line is EU-only. Every seed is created with `create-seed.sh` (the DigitalOcean
|
|
variant reserves an IP in the same way) and provisioned with `provision-seed.sh`, which adds the seeds already in
|
|
`seeds.txt` as `--addpeer` entries so the seeds form a full mesh among themselves. `health.sh` checks them all.
|
|
|
|
## Rotation
|
|
|
|
1. Add before removing: create and provision the replacement, run `health.sh` until it is synced and has peers.
|
|
2. Bake the new list (`params.rs`) and release packages with it; keep the old address in the list for one release so
|
|
clients on the previous build still bootstrap.
|
|
3. Keep the old IP alive until the release after that (a Hetzner primary IP or a DO reserved IP costs under USD 1 per
|
|
month unattached, approximate), then delete the server and the IP, and remove the entry from `seeds.txt` and
|
|
`seeds.tsv`.
|
|
4. A compromised seed is the one case to remove first: delete the server, release the IP, bake and release the same
|
|
day. The damage a bad seed can do is bounded (it hands out addresses; the node's handshake and PoW checks are
|
|
unchanged), which is why the list may sit in a release rather than behind a signature.
|
|
|
|
## Health and operations
|
|
|
|
`health.sh` (one line per seed: p2p port reachable from the Mac, unit active, RPC answering, synced, blocks and
|
|
headers, connected peers, known and banned addresses, disk, memory, version); `--watch` repeats every minute. The
|
|
seed's journal: `ssh -i ~/.ssh/igneum_ed25519 root@188.245.5.161 journalctl -u igneumd -f`. Updating the binary:
|
|
`provision-seed.sh` again (it rebuilds on the VM) or `BUILD_WHERE=bin` to push a binary built by the cloud-devnet
|
|
builder. The database format changes with some fork commits; a seed that refuses to start after an update is wiped
|
|
(`rm -rf /var/lib/igneum/*`) and resyncs from its peers.
|
|
|
|
## No-spend rule
|
|
|
|
Only the first seed spends tonight (approved). The remaining seeds and the 20-node network wait for the morning.
|