| Crates.io | manydns |
| lib.rs | manydns |
| version | 1.0.0 |
| created_at | 2026-01-24 16:50:41.913538+00 |
| updated_at | 2026-01-24 16:50:41.913538+00 |
| description | Provider-agnostic DNS zone and record management, inspired by the Go libdns project |
| homepage | |
| repository | https://github.com/usips/manydns-rs |
| max_upload_size | |
| id | 2066991 |
| size | 493,857 |
A Rust library providing a provider-agnostic API for managing DNS zones and records.
manydns defines abstract traits for DNS zone and record management, with concrete implementations for multiple DNS providers. The API design is inspired by the Go libdns project, adapting its conventions for Rust idioms while maintaining familiar semantics for developers coming from that ecosystem.
Add manydns to your project with the provider you need:
[dependencies]
manydns = { version = "1.0", features = ["cloudflare"] }
use manydns::{Provider, Zone, CreateRecord, DeleteRecord, RecordData};
use manydns::cloudflare::CloudflareProvider;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Create a provider with your API credentials
let provider = CloudflareProvider::new("your_api_token")?;
// Get a zone by domain name
let zone = provider.get_zone("example.com").await?;
println!("Zone: {} (ID: {})", zone.domain(), zone.id());
// List all records in the zone
let records = zone.list_records().await?;
for record in &records {
println!(" {} {} -> {:?}", record.host, record.data.get_type(), record.data);
}
// Create a new A record
let record = zone.create_record(
"www", // host (use "@" for apex)
&RecordData::A("192.0.2.1".parse()?), // record data
300, // TTL in seconds
).await?;
println!("Created record: {}", record.id);
// Delete a record by ID
zone.delete_record(&record.id).await?;
Ok(())
}
Enable providers via feature flags:
| Provider | Feature Flag | Zone Create/Delete |
|---|---|---|
| Cloudflare | cloudflare |
No |
| Hetzner DNS | hetzner |
Yes |
| DNSPod | dnspod |
No |
| Tencent Cloud | tencent |
No |
| Technitium | technitium-dns |
Yes |
| Namecheap | namecheap |
No |
The library uses a capability-based trait design:
// Provider: entry point for zone access
pub trait Provider {
type Zone: Zone;
async fn list_zones(&self) -> Result<Vec<Self::Zone>, ...>;
async fn get_zone(&self, zone_id: &str) -> Result<Self::Zone, ...>;
}
// Zone: record management within a zone
pub trait Zone {
fn id(&self) -> &str;
fn domain(&self) -> &str;
async fn list_records(&self) -> Result<Vec<Record>, ...>;
async fn get_record(&self, record_id: &str) -> Result<Record, ...>;
}
// Optional capabilities
pub trait CreateRecord: Zone { ... }
pub trait DeleteRecord: Zone { ... }
pub trait CreateZone: Provider { ... }
pub trait DeleteZone: Provider { ... }
Supported DNS record types:
Record names are relative to the zone, following the same convention as Go libdns:
"www" refers to www.example.com in zone example.com"@" refers to the zone apex (example.com itself)"sub.domain" refers to sub.domain.example.comProvider implementations use reqwest for HTTP. By default, default-tls is enabled. Alternative backends:
# Use rustls instead of native TLS
manydns = { version = "1.0", default-features = false, features = ["rustls-tls", "cloudflare"] }
Available: default-tls (default), rustls-tls, native-tls, native-tls-vendored
This library adapts the Go libdns API for Rust:
| Go libdns | Rust manydns |
|---|---|
provider.GetRecords(ctx, zone) |
provider.get_zone(zone).await?.list_records().await? |
| Zone as string parameter | Zone as trait object |
Batch operations on []Record |
Single record operations |
| Delete by record content match | Delete by provider-specific ID |
Record naming conventions (@ for apex, relative names) are intentionally identical.
Contributions welcome! See the existing provider implementations for patterns to follow when adding new providers.
0BSD