Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to the `ant` binary will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Changed
- Default network binding changed from IPv4-only to IPv6 dual-stack. Hosts without a working IPv6 stack should pass `--ipv4-only` to avoid advertising unreachable v6 addresses to the DHT (which causes slow connects and junk address records).

## [0.1.1] - 2026-03-28

### Added
Expand Down
6 changes: 6 additions & 0 deletions ant-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ pub struct Cli {
#[arg(long)]
pub allow_loopback: bool,

/// Force IPv4-only mode (disable dual-stack).
/// Use on hosts without working IPv6 to avoid advertising
/// unreachable addresses to the DHT.
#[arg(long)]
pub ipv4_only: bool,

/// Timeout for lightweight network operations such as quotes and DHT
/// lookups (seconds).
#[arg(long, default_value_t = 10)]
Expand Down
14 changes: 10 additions & 4 deletions ant-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ async fn run() -> anyhow::Result<()> {
bootstrap,
devnet_manifest,
allow_loopback,
ipv4_only,
quote_timeout_secs,
store_timeout_secs,
verbose,
Expand All @@ -83,6 +84,7 @@ async fn run() -> anyhow::Result<()> {
bootstrap,
devnet_manifest,
allow_loopback,
ipv4_only,
quote_timeout_secs,
store_timeout_secs,
evm_network,
Expand Down Expand Up @@ -152,6 +154,7 @@ struct DataCliContext {
bootstrap: Vec<SocketAddr>,
devnet_manifest: Option<PathBuf>,
allow_loopback: bool,
ipv4_only: bool,
quote_timeout_secs: u64,
store_timeout_secs: u64,
evm_network: String,
Expand All @@ -178,11 +181,12 @@ async fn build_data_client(

// Connection phase with animated spinner showing peer discovery in real-time
let node = if quiet {
create_client_node(bootstrap, ctx.allow_loopback).await?
create_client_node(bootstrap, ctx.allow_loopback, ctx.ipv4_only).await?
} else {
let spinner = new_spinner("Connecting to autonomi network...");

let node = match create_client_node_raw(bootstrap, ctx.allow_loopback).await {
let node = match create_client_node_raw(bootstrap, ctx.allow_loopback, ctx.ipv4_only).await
{
Ok(n) => n,
Err(e) => {
spinner.finish_and_clear();
Expand Down Expand Up @@ -377,8 +381,9 @@ fn resolve_bootstrap_from(
async fn create_client_node(
bootstrap: Vec<SocketAddr>,
allow_loopback: bool,
ipv4_only: bool,
) -> anyhow::Result<Arc<P2PNode>> {
let node = create_client_node_raw(bootstrap, allow_loopback).await?;
let node = create_client_node_raw(bootstrap, allow_loopback, ipv4_only).await?;
node.start()
.await
.map_err(|e| anyhow::anyhow!("Failed to start P2P node: {e}"))?;
Expand All @@ -389,10 +394,11 @@ async fn create_client_node(
async fn create_client_node_raw(
bootstrap: Vec<SocketAddr>,
allow_loopback: bool,
ipv4_only: bool,
) -> anyhow::Result<Arc<P2PNode>> {
let mut core_config = CoreNodeConfig::builder()
.port(0)
.ipv6(false)
.ipv6(!ipv4_only)
.local(allow_loopback)
.mode(NodeMode::Client)
.max_message_size(MAX_WIRE_MESSAGE_SIZE)
Expand Down
21 changes: 16 additions & 5 deletions ant-core/src/data/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ pub struct ClientConfig {
/// defaults to `false` and threads through to the same
/// `CoreNodeConfig::builder().local(...)` call.
pub allow_loopback: bool,
/// Bind a dual-stack IPv6 socket (`true`) or an IPv4-only socket
/// (`false`). Defaults to `true`, matching the CLI default.
///
/// Set to `false` only when running on hosts without a working IPv6
/// stack, to avoid advertising unreachable v6 addresses to the DHT
/// (which causes slow connects and junk DHT address records). This
/// mirrors the `--ipv4-only` flag in `ant-cli`.
pub ipv6: bool,
}

impl Default for ClientConfig {
Expand All @@ -89,6 +97,7 @@ impl Default for ClientConfig {
quote_concurrency: DEFAULT_QUOTE_CONCURRENCY,
store_concurrency: DEFAULT_STORE_CONCURRENCY,
allow_loopback: false,
ipv6: true,
}
}
}
Expand Down Expand Up @@ -123,9 +132,10 @@ impl Client {

/// Create a client connected to bootstrap peers.
///
/// Threads `config.allow_loopback` through to `Network::new`, which
/// controls the saorsa-transport `local` flag on the underlying
/// `CoreNodeConfig`. See `ClientConfig::allow_loopback` for details.
/// Threads `config.allow_loopback` and `config.ipv6` through to
/// `Network::new`, which controls the saorsa-transport `local` and
/// `ipv6` flags on the underlying `CoreNodeConfig`. See
/// `ClientConfig::allow_loopback` and `ClientConfig::ipv6` for details.
///
/// # Errors
///
Expand All @@ -135,11 +145,12 @@ impl Client {
config: ClientConfig,
) -> Result<Self> {
debug!(
"Connecting to Autonomi network with {} bootstrap peers (allow_loopback={})",
"Connecting to Autonomi network with {} bootstrap peers (allow_loopback={}, ipv6={})",
bootstrap_peers.len(),
config.allow_loopback,
config.ipv6,
);
let network = Network::new(bootstrap_peers, config.allow_loopback).await?;
let network = Network::new(bootstrap_peers, config.allow_loopback, config.ipv6).await?;
Ok(Self {
config,
network,
Expand Down
19 changes: 15 additions & 4 deletions ant-core/src/data/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,27 @@ impl Network {
/// testing. Public Autonomi network peers reject the QUIC handshake
/// variant produced when `local = true`, so production callers must pass
/// `false` (this is what `ant-cli` does by default — see
/// `ant-cli/src/main.rs::create_client_node_raw`, which builds the same
/// `CoreNodeConfig` directly with `.local(allow_loopback)`).
/// `ant-cli/src/main.rs::create_client_node_raw`, which builds a similar
/// `CoreNodeConfig` directly, with `ipv6` toggled by the `--ipv4-only`
/// flag).
///
/// `ipv6` controls whether the node binds a dual-stack IPv6 socket
/// (`true`) or an IPv4-only socket (`false`). The default for library
/// callers should be `true` to match the CLI default; set it to `false`
/// only when running on hosts without a working IPv6 stack, to avoid
/// advertising unreachable v6 addresses to the DHT.
///
/// # Errors
///
/// Returns an error if the P2P node cannot be created or bootstrapping fails.
pub async fn new(bootstrap_peers: &[SocketAddr], allow_loopback: bool) -> Result<Self> {
pub async fn new(
bootstrap_peers: &[SocketAddr],
allow_loopback: bool,
ipv6: bool,
) -> Result<Self> {
let mut core_config = CoreNodeConfig::builder()
.port(0)
.ipv6(true)
.ipv6(ipv6)
.local(allow_loopback)
.mode(NodeMode::Client)
.max_message_size(MAX_WIRE_MESSAGE_SIZE)
Expand Down
3 changes: 3 additions & 0 deletions ant-core/tests/support/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@ impl MiniTestnet {
// Generate ML-DSA-65 identity for this node
let identity = Arc::new(NodeIdentity::generate().expect("generate node identity"));

// IPv4-only is intentional for these loopback-only tests: everything
// binds to 127.0.0.1 on the local host, so dual-stack would add no
// value and pulls in v6 loopback quirks on some CI runners.
let mut core_config = CoreNodeConfig::builder()
.port(listen_addr.port())
.ipv6(false)
Expand Down
Loading