I want to share an idea for potentially simplifying autonomi::Client
configuration based on my recent experience working with it.
Current Configuration
Right now, the Rust autonomi::Client
can be configured in several ways:
- Multiple
init
functions:Client::init() Client::init_local() Client::init_with_peers(peers: Vec<Multiaddr>) Client::init_with_config(config: ClientConfig) // and in the next release afaik Client::init_alpha()
- The
ClientConfig
struct, which has many properties:pub struct ClientConfig { pub init_peers_config: InitialPeersConfig, pub evm_network: EvmNetwork, pub strategy: ClientOperatingStrategy, pub network_id: Option<u8>, } pub InitialPeersConfig { pub first: bool, pub addrs: Vec<Multiaddr>, pub local: bool, pub ignore_cache: bool, pub bootstrap_cache_dir: Option<PathBuf>, } pub enum EvmNetwork { ArbitrumOne, ArbitrumSepolia, ArbitrumSepoliaTest, Custom(CustomNetwork), } pub struct CustomNetwork { pub rpc_url_http: reqwest::Url, pub payment_token_address: Address, pub data_payments_address: Address, }
- Internal environment variables (like
ANT_PEERS
,RPC_URL
, etc.) also affect the finalautonomi::Client
configuration.
In my project, I found configuring the client and especially allowing different configurations at runtime to be more cumbersome that it needs to be.
- It requires quite a bit of extra boilerplate code where I need configurability.
- There are usability considerations: many options need specifying, validating, and user-friendly error reporting.
- When new options like
init_alpha()
are introduced, this code needs updates everywhere.
An Approach I Tried: Configuration via URI
To handle this, I created a higher-level ClientConfig
type in my project. It serializes to and deserializes from a simple, human-readable URI string: autonomi:config:<details>
.
Basic Examples:
autonomi:config:mainnet
autonomi:config:alphanet
autonomi:config:testnet
Custom Local Network Example:
autonomi:config:local?rpc_url=http%3A%2F%2FYOUR_HOST_IP_ADDRESS%3A14143%2F&payment_token_addr=0x...&data_payments_addr=0x...&bootstrap_url=http%3A%2F%2F2FYOUR_HOST_IP_ADDRESS%3A38112%2Fbootstrap.txt
Usage example:
// parse the URI and turn into a valid config
let config = ClientConfig::from_str("autonomi:config:alphanet")?; // ClientConfig implements FromStr
// get a new, fully configured client
let client = config.try_new_client().await?;
// the config can also be turned into a string again
let config_string = config.to_string();
That’s it, nothing else needed. It supports all the options I found in the Client
code.
To get a better idea, here is the link to my current implementation:
It works really well in practice. The string format works easily with all kinds of tools. Here is how I use it with the clap
crate to parse command line arguments:
use clap::Parser;
#[derive(Debug, Parser)]
#[command(version)]
struct Arguments {
/// Autonomi Network Configuration
#[arg(long, short = 'c', env, default_value = "autonomi:config:mainnet")]
autonomi_config: ClientConfig,
}
/// ...
let arguments = Arguments::parse();
let client = (&arguments.autonomi_config).try_new_client().await?;
The above code defaults to mainnet (using claps default_value
) but automatically supports any configuration string the user provides, without extra handling code in my app.
Potential Benefit for autonomi::Client
?
Thinking about this, a similar approach could be beneficial for autonomi::Client
itself?
Thought: Could we replace the different init
functions and environment variables with a single:
Client::init(config_uri: impl AsRef<str>)
Advantages:
- Simplified and standardised configuration: Set up the client with just one standard string (which is also easy to store).
- Future-proof: Application code wouldn’t need updates just because new configuration options are added to the core client library later.
- Easier third-language integration: Maintaining client libraries in other languages would become simpler and more consistent with the Rust core.
- Clearer, explicit configuration: The URI string provides a single, explicit source of truth for the full client config.
This approach really removed friction for me during development, especially when using a local testnet.
What do you guys think? Open to feedback.