pub struct Client(/* private fields */);Expand description
Pkarr client for publishing and resolving SignedPackets over mainline Dht and/or Relays.
Implementations§
Source§impl Client
impl Client
Sourcepub fn as_blocking(&self) -> ClientBlocking
pub fn as_blocking(&self) -> ClientBlocking
Returns a blocking (synchronous ) version of Client.
Source§impl Client
impl Client
Sourcepub fn builder() -> ClientBuilder
pub fn builder() -> ClientBuilder
Returns a builder to edit config before creating Client.
You can use ClientBuilder::no_default_network to start from a clean slate and decide which networks to use.
Sourcepub fn dht(&self) -> Option<Dht>
pub fn dht(&self) -> Option<Dht>
Returns a reference to the internal mainline::Dht node.
Gives you access to methods like mainline::Dht::info, mainline::Dht::bootstrapped, and mainline::Dht::to_bootstrap among the rest of the API.
Sourcepub async fn publish(
&self,
signed_packet: &SignedPacket,
cas: Option<Timestamp>,
) -> Result<(), PublishError>
pub async fn publish( &self, signed_packet: &SignedPacket, cas: Option<Timestamp>, ) -> Result<(), PublishError>
Publishes a SignedPacket to the mainline Dht and or Relays.
§Lost Update Problem
Mainline DHT and remote relays form a distributed network, and like all distributed networks, it is vulnerable to Write–write conflict.
§Read first
To mitigate the risk of lost updates, you should call the Self::resolve_most_recent method then start authoring the new SignedPacket based on the most recent as in the following example:
use pkarr::{Client, SignedPacket, Keypair};
// For local testing
use pkarr::mainline::Testnet;
#[tokio::main]
async fn run() -> anyhow::Result<()> {
let testnet = Testnet::new_async(3).await?;
let client = Client::builder()
// Disable the default network settings (builtin relays and mainline bootstrap nodes).
.no_default_network()
.bootstrap(&testnet.bootstrap)
.build()?;
let keypair = Keypair::random();
let (signed_packet, cas) = if let Some(most_recent) = client
.resolve_most_recent(&keypair.public_key()).await
{
let mut builder = SignedPacket::builder();
// 1. Optionally inherit all or some of the existing records.
for record in most_recent.all_resource_records() {
let name = record.name.to_string();
if name != "foo" && name != "sercert" {
builder = builder.record(record.clone());
}
};
// 2. Optionally add more new records.
let signed_packet = builder
.txt("foo".try_into()?, "bar".try_into()?, 30)
.a("secret".try_into()?, 42.into(), 30)
.sign(&keypair)?;
(
signed_packet,
// 3. Use the most recent [SignedPacket::timestamp] as a `CAS`.
Some(most_recent.timestamp())
)
} else {
(
SignedPacket::builder()
.txt("foo".try_into()?, "bar".try_into()?, 30)
.a("secret".try_into()?, 42.into(), 30)
.sign(&keypair)?,
None
)
};
client.publish(&signed_packet, cas).await?;
Ok(())
}§Errors
This method may return on of these errors:
- QueryError: when the query fails, and you need to retry or debug the network.
- ConcurrencyError: when an write conflict (or the risk of it) is detedcted.
If you get a ConcurrencyError; you should resolver the most recent packet again, and repeat the steps in the previous example.
Sourcepub async fn resolve(&self, public_key: &PublicKey) -> Option<SignedPacket>
pub async fn resolve(&self, public_key: &PublicKey) -> Option<SignedPacket>
Returns a SignedPacket from the cache even if it is expired. If there is no packet in the cache, or if the cached packet is expired, it will make a DHT query in a background query and caches any more recent packets it receives.
If you want to get the most recent version of a SignedPacket, you should use Self::resolve_most_recent.
Sourcepub async fn resolve_most_recent(
&self,
public_key: &PublicKey,
) -> Option<SignedPacket>
pub async fn resolve_most_recent( &self, public_key: &PublicKey, ) -> Option<SignedPacket>
Returns the most recent SignedPacket found after querying all mainline Dht nodes and or Relays.
Useful if you want to read the most recent packet before publishing a new packet.
This is a best effort, and doesn’t guarantee consistency.
Source§impl Client
impl Client
Sourcepub fn resolve_https_endpoints<'a>(
&'a self,
qname: &'a str,
) -> impl Stream<Item = Endpoint> + 'a
pub fn resolve_https_endpoints<'a>( &'a self, qname: &'a str, ) -> impl Stream<Item = Endpoint> + 'a
Sourcepub fn resolve_svcb_endpoints<'a>(
&'a self,
qname: &'a str,
) -> impl Stream<Item = Endpoint> + 'a
pub fn resolve_svcb_endpoints<'a>( &'a self, qname: &'a str, ) -> impl Stream<Item = Endpoint> + 'a
Sourcepub async fn resolve_https_endpoint(
&self,
qname: &str,
) -> Result<Endpoint, CouldNotResolveEndpoint>
pub async fn resolve_https_endpoint( &self, qname: &str, ) -> Result<Endpoint, CouldNotResolveEndpoint>
Helper method that returns the first HTTPS Endpoint in the Async stream from Self::resolve_https_endpoints
Sourcepub async fn resolve_svcb_endpoint(
&self,
qname: &str,
) -> Result<Endpoint, CouldNotResolveEndpoint>
pub async fn resolve_svcb_endpoint( &self, qname: &str, ) -> Result<Endpoint, CouldNotResolveEndpoint>
Helper method that returns the first SVCB Endpoint in the Async stream from Self::resolve_svcb_endpoints
Trait Implementations§
Source§impl From<Client> for CertVerifier
impl From<Client> for CertVerifier
Source§impl From<Client> for ClientBuilder
impl From<Client> for ClientBuilder
Source§fn from(client: Client) -> Self
fn from(client: Client) -> Self
Create a reqwest::ClientBuilder from this Pkarr client, using it as a dns_resolver, and a preconfigured_tls client config that uses rustls::crypto::ring::default_provider() and follows the tls for pkarr domains spec.
Source§impl From<Client> for ClientConfig
impl From<Client> for ClientConfig
Source§fn from(client: Client) -> Self
fn from(client: Client) -> Self
Creates a rustls::ClientConfig that uses rustls::crypto::ring::default_provider() and no client auth and follows the tls for pkarr domains spec.
If you want more control, create a CertVerifier from this Client to use as a custom certificate verifier.