From ab24958f1470cb62a4637126345fdf14c2ec0bd4 Mon Sep 17 00:00:00 2001 From: Bitflash Date: Wed, 29 Jul 2026 16:01:43 -0300 Subject: net: compiled-in bootstrap seeds, so a first run does not need a relay (#36) * net: compiled-in bootstrap seeds, so a first run does not need a relay A node with no cache has only the Nostr relays. If they are down, blocked or slow, a first run has no way into the network at all -- and that is the one moment a user has no patience for. Seeds are the floor under that. A seed entry is an address and an encryption key. Deliberately no meeting node: the rendezvous relay pairs on the service's public key, and a `.btf` address is that key, so a client can find a seed by trying the known relays in turn. Recording which relay a seed was on would rot the moment it failed over, which is exactly the failure this project spent weeks chasing -- advertising a rendezvous somebody is not registered at. Seeds carry no authority. They serve the same signed, self-certifying descriptors as any peer, so a hostile seed can stall a bootstrap but cannot forge a peer or feed a false chain. The compiled list is empty. Which nodes become permanent public bootstrap targets is a decision for whoever runs the network, not a default, and it is close to irreversible once binaries are distributed. Empty is valid: discovery falls back to Nostr exactly as before. /btfseed=ADDRESS:ENCHEX (repeatable) appends at runtime, for testing and for private networks. Tested against the live network, clean datadir, no cache, three seeds carrying no meeting node: btfpeers: no cache yet, falling back to seeds btfseed: trying 3 seed(s) across 3 relay(s) btfseed: reached q5jxyzn...btf via 90.156.222.107:8434 btfseed: 3 seed(s) answered * net: add the Almaty bootstrap node to the seed list Dedicated node, no wallet balance, no mining, running beside the rendezvous relay on the same host. It exists only to answer a first dial. Fresh datadir, no flags, compiled list only: btfpeers: no cache yet, falling back to seeds btfseed: trying 1 seed(s) across 3 relay(s) btfseed: reached ygffz37...btf via 90.156.222.107:8434 btfseed: 1 seed(s) answered btfpeers: kept 2 of 2 descriptor(s) 5.2 seconds from nothing to a peer, against 36s for a cold start that had to go through the relays. The seed then handed over two more peers by exchange, which the cache keeps for next time. --- src/main_gui.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src/main_gui.cpp') diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 63bdbb7..9f7e34e 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -61,6 +61,7 @@ static void PrintUsage() printf("\n"); printf("Network:\n"); printf(" /port=N (P2P listen port, default 8433)\n"); + printf(" /btfseed=ADDRESS:ENCHEX (extra bootstrap peer, repeatable)\n"); printf("\n"); printf("Each option also accepts '-' instead of '/'.\n"); } @@ -117,6 +118,25 @@ static void ParseStartupArguments(int argc, char* argv[]) if (!announceRelay.empty()) strBtfAnnounceRelay = announceRelay; + // /btfseed=ADDRESS:ENCHEX -- extra bootstrap peers, repeatable. Useful for + // testing the seed path and for private networks that ship no compiled list. + for (int i = 1; i < argc; i++) + { + string s = argv[i]; + size_t eq = s.find('='); + if (eq == string::npos) continue; + string key = s.substr(0, eq); + if (key != "/btfseed" && key != "-btfseed") continue; + string val = s.substr(eq + 1); + size_t colon = val.rfind(':'); + if (colon == string::npos || colon + 1 >= val.size()) + { + fprintf(stderr, "Ignoring %s: expected ADDRESS:ENCHEX\n", s.c_str()); + continue; + } + vBtfExtraSeeds.push_back(make_pair(val.substr(0, colon), val.substr(colon + 1))); + } + // net.cpp has described nListenPort as "tunable via /port" since it was // written, but nothing ever read the option, so the port was fixed at 8433 // and a second node could not start on a machine already running one. -- cgit v1.2.3