Skip to content

Commit 3a40c14

Browse files
jacderidaclaude
andcommitted
feat: add bootstrap peers auto-discovery from shipped config file
Introduce `bootstrap_peers.toml`, a dedicated config file that provides initial bootstrap peer addresses for joining the production network. The file is auto-discovered on startup from well-known locations (next to the binary, platform config dir, /etc/ant/) when no --bootstrap CLI argument is provided. - Add BootstrapPeersConfig struct with TOML parsing and search-path discovery - Integrate into Cli::into_config() with clear precedence: --bootstrap CLI > --config file > auto-discovered bootstrap_peers.toml - Log which bootstrap source was used on startup - Ship bootstrap_peers.toml in release archives alongside binaries - Add 7 production bootstrap node addresses - Add unit tests for parsing, discovery, and edge cases - Update README with bootstrap peers file format and search paths - Clean up stale saorsa bootstrap references from CLAUDE.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4df64b4 commit 3a40c14

7 files changed

Lines changed: 364 additions & 59 deletions

File tree

.github/workflows/release.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,15 +166,19 @@ jobs:
166166
- name: Create archive (Unix)
167167
if: matrix.archive == 'tar.gz'
168168
run: |
169+
cp config/bootstrap_peers.toml target/${{ matrix.target }}/release/
169170
cd target/${{ matrix.target }}/release
170-
tar -czvf ../../../ant-node-cli-${{ matrix.friendly_name }}.tar.gz ${{ matrix.binary }} ant-keygen
171+
tar -czvf ../../../ant-node-cli-${{ matrix.friendly_name }}.tar.gz ${{ matrix.binary }} ant-keygen bootstrap_peers.toml
171172
cd ../../..
172173
173174
- name: Create archive (Windows)
174175
if: matrix.archive == 'zip'
175176
shell: pwsh
176177
run: |
177-
Compress-Archive -Path "target/${{ matrix.target }}/release/${{ matrix.binary }}", "target/${{ matrix.target }}/release/ant-keygen.exe" -DestinationPath "ant-node-cli-${{ matrix.friendly_name }}.zip"
178+
Copy-Item "config/bootstrap_peers.toml" "target/${{ matrix.target }}/release/bootstrap_peers.toml"
179+
Push-Location "target/${{ matrix.target }}/release"
180+
Compress-Archive -Path "${{ matrix.binary }}", "ant-keygen.exe", "bootstrap_peers.toml" -DestinationPath "../../../ant-node-cli-${{ matrix.friendly_name }}.zip"
181+
Pop-Location
178182
179183
- name: Upload artifact
180184
uses: actions/upload-artifact@v4

CLAUDE.md

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@ cargo run --release -- --listen 0.0.0.0:10000 --bootstrap
3232
# Run additional instance (use any port in range 10000-10999)
3333
cargo run --release -- --listen 0.0.0.0:10001
3434

35-
# Run as regular node connecting to bootstrap
36-
cargo run --release -- --listen 0.0.0.0:10000 --connect saorsa-2.saorsalabs.com:10000
37-
3835
# Run with debug logging
3936
RUST_LOG=debug cargo run --release -- --listen 0.0.0.0:10000
4037
```
@@ -97,31 +94,15 @@ When testing or developing ant-node:
9794
# CORRECT - ant-node operations (within 10000-10999)
9895
cargo run --release -- --listen 0.0.0.0:10000
9996
cargo run --release -- --listen 0.0.0.0:10001 # Second instance OK
100-
ssh root@saorsa-2.saorsalabs.com "systemctl restart ant-node-bootstrap"
101-
102-
# WRONG - Would disrupt other networks
103-
ssh root@saorsa-2.saorsalabs.com "pkill -f ':9'" # NEVER - matches ant-quic ports
104-
ssh root@saorsa-2.saorsalabs.com "pkill -f ':11'" # NEVER - matches communitas ports
105-
ssh root@saorsa-2.saorsalabs.com "systemctl restart ant-quic-bootstrap" # NOT OUR SERVICE
10697
```
10798

108-
### Bootstrap Endpoints (ant-node)
109-
```
110-
saorsa-2.saorsalabs.com:10000 (NYC - 142.93.199.50)
111-
saorsa-3.saorsalabs.com:10000 (SFO - 147.182.234.192)
112-
```
99+
### Bootstrap Peers Configuration
100+
101+
Production bootstrap peer addresses are defined in `config/bootstrap_peers.toml`.
102+
This file is shipped alongside the binary in release archives and is auto-discovered
103+
on startup when no `--bootstrap` CLI argument is provided.
113104

114105
### Before Any VPS Operations
115106
1. Verify you're targeting ports 10000-10999 only
116107
2. Double-check service names contain "ant-node"
117108
3. Never run broad `pkill` commands that could affect other services
118-
119-
### Deploy New Binary
120-
```bash
121-
# Build release binary
122-
cargo build --release
123-
124-
# Deploy to bootstrap node
125-
scp target/release/ant-node root@saorsa-2.saorsalabs.com:/opt/ant-node/
126-
ssh root@saorsa-2.saorsalabs.com "systemctl restart ant-node-bootstrap"
127-
```

README.md

Lines changed: 68 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -739,19 +739,32 @@ For production deployments, run ant-node under a service manager so it restarts
739739

740740
### systemd (Linux)
741741

742-
A service file is included at `systemd/ant-node.service` and installed automatically by RPM/DEB packages.
742+
Create a service file at `/etc/systemd/system/ant-node.service`:
743+
744+
```ini
745+
[Unit]
746+
Description=Ant Node
747+
After=network-online.target
748+
Wants=network-online.target
749+
750+
[Service]
751+
Type=simple
752+
ExecStart=/usr/local/bin/ant-node --stop-on-upgrade
753+
Restart=always
754+
RestartSec=5s
755+
756+
[Install]
757+
WantedBy=multi-user.target
758+
```
743759

744760
```bash
745-
# Install and enable
746-
sudo cp systemd/ant-node.service /etc/systemd/system/
747761
sudo systemctl daemon-reload
748762
sudo systemctl enable --now ant-node
749763
```
750764

751765
Key settings:
752766
- `--stop-on-upgrade` tells the node to exit cleanly after applying an upgrade
753767
- `Restart=always` ensures systemd restarts the node after the upgrade exit
754-
- Security hardening (NoNewPrivileges, ProtectSystem, etc.) is pre-configured
755768

756769
### launchd (macOS)
757770

@@ -869,24 +882,25 @@ cargo build --release
869882
./target/release/ant-node
870883
```
871884

872-
### Join Testnet
885+
### Join the Network
886+
887+
On first startup, the node needs bootstrap peers to join the network.
888+
Production bootstrap peers are shipped in `bootstrap_peers.toml` alongside
889+
the binary and are loaded automatically:
873890

874891
```bash
875-
# Connect to testnet bootstrap nodes
876-
./target/release/ant-node --testnet
892+
# Starts and auto-discovers bootstrap_peers.toml next to the binary
893+
./target/release/ant-node
894+
```
895+
896+
You can also specify bootstrap peers explicitly:
877897

878-
# Or specify bootstrap peers manually
898+
```bash
879899
./target/release/ant-node \
880-
--bootstrap "165.22.4.178:12000" \
881-
--bootstrap "164.92.111.156:12000"
900+
--bootstrap "10.0.0.1:10000" \
901+
--bootstrap "10.0.0.2:10000"
882902
```
883903

884-
**Testnet Bootstrap Nodes:**
885-
| Node | Location | Address |
886-
|------|----------|---------|
887-
| ant-bootstrap-1 | NYC (DigitalOcean) | `165.22.4.178:12000` |
888-
| ant-bootstrap-2 | SFO (DigitalOcean) | `164.92.111.156:12000` |
889-
890904
### Full Configuration
891905

892906
```bash
@@ -920,8 +934,8 @@ Options:
920934
[default: dual]
921935
922936
--bootstrap <ADDR>
923-
Bootstrap peer multiaddresses (can be specified multiple times)
924-
Example: /ip4/1.2.3.4/udp/12000/quic-v1
937+
Bootstrap peer socket addresses (can be specified multiple times)
938+
Example: 1.2.3.4:10000
925939
926940
--migrate-ant-data <PATH>
927941
Path to legacy ant-node data directory to migrate
@@ -948,9 +962,41 @@ Options:
948962

949963
Configuration sources (highest to lowest priority):
950964

951-
1. **Command-line arguments**
965+
1. **Command-line arguments** (e.g., `--bootstrap`)
952966
2. **Environment variables** (`ANT_*`)
953-
3. **Configuration file** (`~/.ant-node/config.toml`)
967+
3. **Configuration file** (`--config path/to/config.toml`)
968+
4. **Bootstrap peers file** (auto-discovered `bootstrap_peers.toml`)
969+
970+
### Bootstrap Peers File
971+
972+
The `bootstrap_peers.toml` file provides initial peers for joining the network.
973+
It is shipped alongside the binary in release archives and auto-discovered on startup
974+
when no `--bootstrap` CLI argument is provided.
975+
976+
**Format:**
977+
```toml
978+
peers = [
979+
"10.0.0.1:10000",
980+
"10.0.0.2:10000",
981+
]
982+
```
983+
984+
**Search order** (first match wins):
985+
986+
| Priority | Source | Path |
987+
|----------|--------|------|
988+
| 1 | `$ANT_BOOTSTRAP_PEERS_PATH` env var | Explicit path to file |
989+
| 2 | Executable directory | `<exe_dir>/bootstrap_peers.toml` |
990+
| 3 | Platform config dir | Linux: `~/.config/ant/bootstrap_peers.toml` |
991+
| | | macOS: `~/Library/Application Support/ant/bootstrap_peers.toml` |
992+
| | | Windows: `%APPDATA%\ant\bootstrap_peers.toml` |
993+
| 4 | System config (Unix) | `/etc/ant/bootstrap_peers.toml` |
994+
995+
**Bootstrap peer precedence** (highest wins):
996+
1. `--bootstrap` CLI argument or `ANT_BOOTSTRAP` env var
997+
2. `bootstrap = [...]` in a `--config` file
998+
3. Auto-discovered `bootstrap_peers.toml`
999+
4. Empty (node cannot join an existing network)
9541000

9551001
### Environment Variables
9561002

@@ -968,15 +1014,12 @@ export ANT_UPGRADE_CHANNEL=stable
9681014
`~/.ant-node/config.toml`:
9691015

9701016
```toml
971-
[node]
9721017
root_dir = "~/.ant-node"
9731018
port = 0 # Auto-select
974-
975-
[network]
9761019
ip_version = "dual"
9771020
bootstrap = [
978-
"/ip4/165.22.4.178/udp/12000/quic-v1",
979-
"/ip4/164.92.111.156/udp/12000/quic-v1"
1021+
"10.0.0.1:10000",
1022+
"10.0.0.2:10000",
9801023
]
9811024

9821025
[upgrade]

config/bootstrap_peers.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Autonomi Network — Bootstrap Peers
2+
#
3+
# This file provides initial peers for joining the production network.
4+
# It is loaded automatically when no --bootstrap CLI argument is provided.
5+
#
6+
# Format: "ip:port" socket addresses.
7+
# Port range for ant-node: 10000-10999.
8+
9+
peers = [
10+
"207.148.94.42:10000",
11+
"45.77.50.10:10000",
12+
"66.135.23.83:10000",
13+
"149.248.9.2:10000",
14+
"167.235.201.229:10000",
15+
"178.156.129.121:10000",
16+
"18.228.202.183:10000",
17+
]

src/bin/ant-node/cli.rs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! Command-line interface definition.
22
33
use ant_node::config::{
4-
BootstrapCacheConfig, EvmNetworkConfig, IpVersion, NetworkMode, NodeConfig, PaymentConfig,
5-
UpgradeChannel,
4+
BootstrapCacheConfig, BootstrapPeersConfig, BootstrapSource, EvmNetworkConfig, IpVersion,
5+
NetworkMode, NodeConfig, PaymentConfig, UpgradeChannel,
66
};
77
use clap::{Parser, ValueEnum};
88
use std::net::SocketAddr;
@@ -185,30 +185,45 @@ pub enum CliNetworkMode {
185185
}
186186

187187
impl Cli {
188-
/// Convert CLI arguments into a `NodeConfig`.
188+
/// Convert CLI arguments into a `NodeConfig` and the source of bootstrap peers.
189+
///
190+
/// # Bootstrap peer precedence (highest to lowest)
191+
///
192+
/// 1. `--bootstrap` CLI argument (or `ANT_BOOTSTRAP` env var)
193+
/// 2. `bootstrap` field in a `--config` file
194+
/// 3. Auto-discovered `bootstrap_peers.toml` from well-known paths
195+
/// 4. Empty list
189196
///
190197
/// # Errors
191198
///
192199
/// Returns an error if a config file is specified but cannot be loaded.
193-
pub fn into_config(self) -> color_eyre::Result<NodeConfig> {
200+
pub fn into_config(self) -> color_eyre::Result<(NodeConfig, BootstrapSource)> {
194201
// Start with default config or load from file
202+
let has_config_file = self.config.is_some();
195203
let mut config = if let Some(ref path) = self.config {
196204
NodeConfig::from_file(path)?
197205
} else {
198206
NodeConfig::default()
199207
};
200208

209+
// Track whether CLI provided bootstrap peers.
210+
let cli_bootstrap_provided = !self.bootstrap.is_empty();
211+
201212
// Override with CLI arguments
202213
if let Some(root_dir) = self.root_dir {
203214
config.root_dir = root_dir;
204215
}
205216

206217
config.port = self.port;
207218
config.ip_version = self.ip_version.into();
208-
config.bootstrap = self.bootstrap;
209219
config.log_level = self.log_level.into();
210220
config.network_mode = self.network_mode.into();
211221

222+
// Apply CLI bootstrap peers if provided; otherwise keep config file value.
223+
if cli_bootstrap_provided {
224+
config.bootstrap = self.bootstrap;
225+
}
226+
212227
// Upgrade config
213228
config.upgrade.channel = self.upgrade_channel.into();
214229
config.upgrade.stop_on_upgrade = self.stop_on_upgrade;
@@ -229,7 +244,25 @@ impl Cli {
229244
..config.bootstrap_cache
230245
};
231246

232-
Ok(config)
247+
// Determine bootstrap source and apply auto-discovery if needed.
248+
let bootstrap_source = if cli_bootstrap_provided {
249+
BootstrapSource::Cli
250+
} else if !config.bootstrap.is_empty() && has_config_file {
251+
BootstrapSource::ConfigFile
252+
} else if config.bootstrap.is_empty() {
253+
// No peers from CLI or config file — try auto-discovery.
254+
if let Some((peers_config, path)) = BootstrapPeersConfig::discover() {
255+
config.bootstrap = peers_config.peers;
256+
BootstrapSource::AutoDiscovered(path)
257+
} else {
258+
BootstrapSource::None
259+
}
260+
} else {
261+
// Config had peers from default (e.g., testnet preset) but no --config file.
262+
BootstrapSource::None
263+
};
264+
265+
Ok((config, bootstrap_source))
233266
}
234267
}
235268

src/bin/ant-node/main.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
mod cli;
44
mod platform;
55

6+
use ant_node::config::BootstrapSource;
67
use ant_node::NodeBuilder;
78
use clap::Parser;
89
use cli::{Cli, CliLogFormat};
@@ -97,7 +98,32 @@ async fn main() -> color_eyre::Result<()> {
9798
};
9899

99100
// Build configuration
100-
let config = cli.into_config()?;
101+
let (config, bootstrap_source) = cli.into_config()?;
102+
103+
match &bootstrap_source {
104+
BootstrapSource::Cli => {
105+
info!(
106+
count = config.bootstrap.len(),
107+
"Bootstrap peers provided via CLI"
108+
);
109+
}
110+
BootstrapSource::ConfigFile => {
111+
info!(
112+
count = config.bootstrap.len(),
113+
"Bootstrap peers loaded from config file"
114+
);
115+
}
116+
BootstrapSource::AutoDiscovered(path) => {
117+
info!(
118+
count = config.bootstrap.len(),
119+
path = %path.display(),
120+
"Bootstrap peers loaded from discovered config"
121+
);
122+
}
123+
BootstrapSource::None => {
124+
warn!("No bootstrap peers configured — node will not be able to join an existing network");
125+
}
126+
}
101127

102128
// Build and run the node
103129
let mut node = NodeBuilder::new(config).build().await?;

0 commit comments

Comments
 (0)