Skip to main content

RegistryClient

Struct RegistryClient 

Source
pub struct RegistryClient { /* private fields */ }
Expand description

HTTP client for a single ACDP registry.

reqwest::Client clones cheaply (it’s an Arc internally), so this struct is Clone to enable per-authority caching in crate::CrossRegistryResolver without re-wiring HTTP+TLS state on every hop. The same is true of budget (issue #258): it is an Option<DiscoveryBudget>, and DiscoveryBudget is itself an Arc handle, so cloning a client that carries a budget shares the counters rather than resetting them — the shape RevocationDiscovery depends on: verify_retrieved attaches one DiscoveryBudget to a single client clone and hands that SAME clone to both trust-class lookups.

revocation_cache (issue #257) is the same shape again: an Option<RevocationCache>, itself an Arc handle, so cloning a client that carries a cache shares the underlying store rather than resetting it. Unlike budget, it is caller-injectable via Self::with_revocation_cache on ANY client, not only the discovery-scoped clone verify_retrieved builds internally — a caller verifying many contexts against the same producer attaches one cache up front and every subsequent discovery benefits. revocation_freshness travels alongside it: verify_retrieved overrides it per call from RevocationDiscovery::freshness (crate-private with_revocation_freshness, crate-private), while a cache attached directly via with_revocation_cache defaults to Duration::ZERO — pure seeding, no marker ever suppresses a lookup, until the caller explicitly opts in through a discovery configuration’s freshness field.

Implementations§

Source§

impl RegistryClient

Source

pub fn authority(&self) -> Option<String>

The authority (host, plus port when non-default) of the registry this client talks to — the value a receipt’s registry_did must match (RFC-ACDP-0010 serving-authority cross-check).

Source

pub fn with_revocation_cache(&self, cache: RevocationCache) -> Self

Return a clone of this client with cache attached (issue #257), replacing any cache the original carried, and resetting revocation_freshness to Duration::ZERO — pure seeding by default, following crate::verified::RevocationDiscovery’s own zero default. Attach a cache once and reuse the returned client across many VerifiedContext::fetch* calls against the same producer(s) to amortize RFC-ACDP-0014 §8 discovery: facts persist (indefinitely, per §7:114) across calls regardless of freshness; setting a discovery configuration’s freshness above zero additionally lets a fresh marker skip a repeat lookup entirely — see crate::revocation_cache for the two-object model.

Source

pub fn with_auth_provider(&self, provider: Arc<dyn AuthProvider>) -> Self

Return a clone of this client that presents read credentials from provider (RFC-ACDP-0008 §6.2, bearer_jwt), replacing any provider the original carried. The provider is asked once per read request, with this client’s Self::authority; see crate::auth for which requests carry a token and why writes never do.

A registry with anonymous_public_reads: false answers anonymous reads not_authorized, which makes revocation discovery against it unknown (unanswerable); a configured token makes it answerable.

Source

pub fn has_auth_provider(&self) -> bool

Whether this client carries an AuthProvider.

Source

pub fn new(base_url: &str) -> Result<Self, AcdpError>

Connect to a registry at base_url (e.g. https://registry.example.com).

Uses rustls for TLS; does not use the system OpenSSL. Applies the RFC-ACDP-0006 §7.4 default timeouts (5s connect, 30s total) and §7.5 redirect policy (max 3 follows, same authority only).

§DNS-rebinding posture (default)

This constructor installs the SafeDnsResolver DNS hook (RFC-ACDP-0006 §7.6): every hostname lookup — the first connect, each redirect, and every reconnect the pool makes over the client’s lifetime — is filtered through the SsrfPolicy at DNS time, before any TCP connect. This is strictly stronger than pin-once resolution (Self::new_pinned): a pinned client validates a single answer and reuses that address, so a hostile authoritative DNS server that only later flips a name into a forbidden range is still caught here but not there. The DNS-hook posture is therefore the default for all callers; reach for Self::builder only when you need a non-default knob (a private root cert, a custom SsrfPolicy, timeout overrides, or the legacy pinned mode).

Source

pub fn builder(base_url: &str) -> RegistryClientBuilder

Start a RegistryClientBuilder for the non-default connection postures — a private root certificate, a custom SsrfPolicy, timeout overrides, and the legacy pinned-resolution mode.

The builder’s default is identical to Self::new: the stronger SafeDnsResolver DNS-hook posture with the default SSRF policy and the RFC-ACDP-0006 §7.4 timeouts. Opt into pin-once resolution with RegistryClientBuilder::pinned.

Source

pub fn with_root_cert_pem(base_url: &str, pem: &[u8]) -> Result<Self, AcdpError>

Connect to a registry that trusts the given PEM-encoded root certificate in addition to the system roots.

Primary use is the in-process self-signed HTTPS server in the crate’s tests/helpers/tls_did_server.rs harness so the spec fixtures fed-001..006 can drive CrossRegistryResolver end-to-end without going over the network.

Source

pub async fn new_pinned( base_url: &str, policy: &SsrfPolicy, ) -> Result<Self, AcdpError>

👎Deprecated since 0.4.0:

prefer RegistryClient::new (SafeDnsResolver DNS hook — validates every connection, strictly stronger than pin-once) or, for explicit pin-once mode, RegistryClient::builder(base_url).pinned(true).ssrf_policy(policy).build().await

Connect to a registry with pin-once DNS-rebinding protection (RFC-ACDP-0006 §7.6).

Resolves the hostname once, validates the resolved IP against policy, then pins that IP into the HTTP client.

Deprecated: the default Self::new posture installs the SafeDnsResolver DNS hook, which validates the resolved IP on every connection (including reconnects) rather than just once — strictly stronger protection. For the rare case that still wants pin-once semantics with a custom policy, use RegistryClient::builder(base_url).pinned(true).ssrf_policy(policy).build().await.

Source

pub async fn capabilities(&self) -> Result<CapabilitiesDocument, AcdpError>

Fetch the registry’s capabilities document and run the RFC-ACDP-0007 §3 runtime validation (acdp_validation::validate_capabilities).

Body capped at 64 KB per RFC-ACDP-0006 §7.3.

Source

pub async fn capabilities_with_ttl( &self, ) -> Result<(CapabilitiesDocument, Duration), AcdpError>

Like Self::capabilities but also returns the cache TTL derived from the response’s Cache-Control: max-age=N header.

Per RFC-ACDP-0006 §4.2, consumers SHOULD cache the capabilities document for min(max-age, 3600s) seconds. When no Cache-Control (or no parseable max-age) is returned, the fallback is 300s — a conservative middle-ground that matches crate::ResolverOptions::capabilities_ttl’s default.

Source

pub async fn publish( &self, req: &PublishRequest, ) -> Result<PublishResponse, AcdpError>

Publish a context. Returns the registry-assigned identifiers.

Source

pub async fn publish_idempotent( &self, req: &PublishRequest, idempotency_key: &str, ) -> Result<PublishResponse, AcdpError>

Publish with an idempotency key for safe retries.

Source

pub async fn publish_with_retry( &self, req: &PublishRequest, idempotency_key: &str, max_attempts: u32, ) -> Result<PublishResponse, AcdpError>

Publish with bounded retry for transient failures.

Reuses idempotency_key across attempts so the registry can dedupe (RFC-ACDP-0003 §6). Retries only when the error is transient per AcdpError::is_transient. Bounded backoff: 250 ms, 500 ms, 1 s, 2 s.

Source

pub async fn retrieve(&self, ctx_id: &CtxId) -> Result<FullContext, AcdpError>

Retrieve a full context (body + registry_state) by ctx_id.

Body capped at 1 MB per RFC-ACDP-0006 §7.3.

Source

pub async fn retrieve_with_metadata( &self, ctx_id: &CtxId, ) -> Result<(FullContext, RetrievalMetadata), AcdpError>

Retrieve a full context plus cache / integrity headers.

Source

pub async fn retrieve_if_none_match( &self, ctx_id: &CtxId, etag: &str, ) -> Result<Option<(FullContext, RetrievalMetadata)>, AcdpError>

Conditional retrieval using If-None-Match.

Returns Ok(None) when the registry responds 304 Not Modified. Returns Ok(Some((body, metadata))) for a fresh retrieval.

Source

pub async fn retrieve_body(&self, ctx_id: &CtxId) -> Result<Body, AcdpError>

Retrieve just the body (immutable, highly cacheable).

Source

pub async fn lineage( &self, lineage_id: &LineageId, ) -> Result<Vec<FullContext>, AcdpError>

Retrieve all contexts in a lineage (oldest to newest).

Source

pub async fn current( &self, lineage_id: &LineageId, ) -> Result<FullContext, AcdpError>

Retrieve the current (latest) context in a lineage.

Source

pub async fn changes( &self, since: &Since, filter: &WatchFilter, limit: Option<u32>, ) -> Result<ChangesResponse, AcdpError>

One page of the registry’s changes feed (GET /changes, RFC-ACDP-0018 §5): the entries after since that match filter, at most limit of them (the registry default, 100, when None).

The response is checked against the closed schema (every entry, cursor bounds, ascending seq), not verified: each entry is a hint until crate::recall::verify_change passes. A cursor older than the registry’s retention is AcdpError::CursorExpired: the consumer has missed an unknown set of entries, must re-verify every watched context directly, then resume from crate::Since::Head (§7 “Resync”). Body capped at 1 MB.

Source

pub async fn search( &self, params: &SearchParams, ) -> Result<SearchResponse, AcdpError>

Keyword search across the registry.

Body capped at 64 KB (search responses are projection-summaries — IMP-03: not the 1 MB context cap).

Source

pub fn search_builder(&self) -> RegistrySearch<'_>

Begin a fluent search via RegistrySearch. Chains parameters with strong typing, then .send().await issues the request.

let resp = client
    .search_builder()
    .q("market risk")
    .tag("risk")
    .tag("portfolio")
    .limit(50)
    .send()
    .await?;

Trait Implementations§

Source§

impl Clone for RegistryClient

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more