pub struct WebResolver { /* private fields */ }Expand description
Resolves did:web:… DIDs to DID documents via HTTPS.
Caches resolved documents for 5–24 hours per §5.11 guidance, evicting the least-recently-used entry once the cache reaches the configured capacity (default 1000).
Every resolution URL passes the SsrfPolicy gate before any socket
activity: a producer-controlled did:web authority is an SSRF vector
identical to a cross-registry reference (RFC-ACDP-0008 §4.8). The
default policy refuses IP-literal authorities and non-HTTPS schemes,
so did:web:127.0.0.1 / did:web:169.254.169.254 cannot turn a
registry verifying a publish — or a consumer verifying a retrieved
context — into an SSRF proxy against process-internal listeners.
Implementations§
Source§impl WebResolver
impl WebResolver
Sourcepub fn new() -> Self
pub fn new() -> Self
Build a resolver with the default LRU capacity (1000 entries).
§Panics
Panics if the underlying HTTP client cannot be built (e.g. the
TLS backend fails to initialize). Use Self::try_new to
handle that failure as a Result instead.
Sourcepub fn try_new() -> Result<Self, AcdpError>
pub fn try_new() -> Result<Self, AcdpError>
Fallible variant of Self::new: builds a resolver with the
default LRU capacity (1000 entries), returning an error instead
of panicking if the underlying HTTP client cannot be built.
Sourcepub fn with_capacity(capacity: usize) -> Self
pub fn with_capacity(capacity: usize) -> Self
Build a resolver with a custom LRU capacity.
§Panics
Panics if capacity == 0 — use a positive capacity; the LRU
model has no semantically valid empty configuration — or if the
underlying HTTP client cannot be built. Use
Self::try_with_capacity to handle the HTTP-client failure
as a Result instead.
Sourcepub fn try_with_capacity(capacity: usize) -> Result<Self, AcdpError>
pub fn try_with_capacity(capacity: usize) -> Result<Self, AcdpError>
Fallible variant of Self::with_capacity: returns an error
instead of panicking if the underlying HTTP client cannot be
built.
§Panics
Still panics if capacity == 0; that is a programmer error, not
a runtime condition.
Sourcepub fn with_root_cert_pem(pem: &[u8]) -> Result<Self, AcdpError>
pub fn with_root_cert_pem(pem: &[u8]) -> Result<Self, AcdpError>
Build a resolver 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 pub-001 / pub-006 / fed-001..006 can drive the
resolver end-to-end without going over the network. Production
callers on corporate intranets MAY also use this to trust a
private CA.
Sourcepub fn with_capacity_and_root_cert_pem(
capacity: usize,
pem: &[u8],
) -> Result<Self, AcdpError>
pub fn with_capacity_and_root_cert_pem( capacity: usize, pem: &[u8], ) -> Result<Self, AcdpError>
Build a resolver with a custom LRU capacity AND a custom root cert.
Sourcepub fn with_ssrf_policy(self, policy: SsrfPolicy) -> Self
pub fn with_ssrf_policy(self, policy: SsrfPolicy) -> Self
Override the SsrfPolicy applied to did:web resolution.
The policy gates both the URL stage (refusing IP-literal authorities and non-HTTPS schemes — fixtures did-ssrf-001/002/003) and the DNS resolution stage (filtering hostnames that resolve into forbidden ranges — RFC-ACDP-0008 §4.8 DNS-rebinding protection). Calling this rebuilds the underlying HTTP client so the DNS resolver hook reflects the new policy.
Relax the policy only in a test harness that resolves
did:web:localhost… against an in-process loopback server.
Production callers MUST keep the default.
§Panics
Panics if the underlying HTTP client cannot be rebuilt. Use
Self::try_with_ssrf_policy to handle that failure as a
Result instead.
Sourcepub fn try_with_ssrf_policy(self, policy: SsrfPolicy) -> Result<Self, AcdpError>
pub fn try_with_ssrf_policy(self, policy: SsrfPolicy) -> Result<Self, AcdpError>
Fallible variant of Self::with_ssrf_policy: returns an error
instead of panicking if the underlying HTTP client cannot be
rebuilt.
Sourcepub fn with_forced_refresh_interval(self, interval: Duration) -> Self
pub fn with_forced_refresh_interval(self, interval: Duration) -> Self
Set the minimum interval between two forced re-fetches of the
same DID (default DEFAULT_FORCED_REFRESH_INTERVAL, one hour).
Duration::ZERO disables the rate limit.
Sourcepub fn forced_refresh_interval(&self) -> Duration
pub fn forced_refresh_interval(&self) -> Duration
The configured forced-refresh interval.
Sourcepub async fn resolve(&self, did: &str) -> Result<DidDocument, AcdpError>
pub async fn resolve(&self, did: &str) -> Result<DidDocument, AcdpError>
Resolve a did:web:… DID to a DID document.
Hits the cache on repeated calls for the same DID. Verifiers that
need the RFC-ACDP-0001 §5.11 “refresh on plausible rotation”
behavior use Self::resolve_until.
Sourcepub async fn resolve_tracked(
&self,
did: &str,
) -> Result<ResolvedDidDocument, AcdpError>
pub async fn resolve_tracked( &self, did: &str, ) -> Result<ResolvedDidDocument, AcdpError>
Self::resolve, also reporting when the document was fetched.
Sourcepub async fn resolve_fresh(&self, did: &str) -> Result<DidDocument, AcdpError>
pub async fn resolve_fresh(&self, did: &str) -> Result<DidDocument, AcdpError>
Forced re-fetch: bypass the cache, fetch the document over HTTPS and repopulate the cache (RFC-ACDP-0001 §5.11 “Caching”).
Rate-limited per DID to one forced fetch per
Self::forced_refresh_interval. A refused forced fetch returns
AcdpError::KeyResolutionUnreachable (transient): the caller
could not obtain a fresh document, which is not evidence about
the key.
Sourcepub async fn resolve_until<F>(
&self,
did: &str,
since: Instant,
accept: F,
) -> Result<FreshResolution, AcdpError>
pub async fn resolve_until<F>( &self, did: &str, since: Instant, accept: F, ) -> Result<FreshResolution, AcdpError>
Resolve did for an operation that started at since, accepting
the first document for which accept returns true.
This is the RFC-ACDP-0001 §5.11 “Caching” (0.5.0) rule made mechanical:
- Resolve normally (cache allowed). If
acceptpasses, done. - If the document was fetched at or after
since, it is already fresh for this operation: return it,fresh = true(the verdict against it is final). - If another task re-fetched the document after
since, use that copy (fresh = true) — concurrent verifications share one forced fetch. - Otherwise force one re-fetch, subject to the per-DID rate
limit, and return the fresh document (
fresh = true). When the rate limit refuses, the cached copy is returned withfresh = false; the caller decides whether that is final (for example, a key present but not inassertionMethod) or must be reported as transient (a key absent from the cached document).
Sourcepub async fn resolve_with_fragment(
&self,
did: &str,
fragment: &str,
since: Instant,
) -> Result<DidDocument, AcdpError>
pub async fn resolve_with_fragment( &self, did: &str, fragment: &str, since: Instant, ) -> Result<DidDocument, AcdpError>
Resolve did and return a document that contains a
verificationMethod with #fragment, re-fetching once (per
Self::resolve_until) when the cached copy lacks it.
Errors when the key is still absent:
AcdpError::KeyResolution(permanent) — absent from a document fetched at or aftersince: the RFC-ACDP-0001 §5.11 step 4 verdict, final per “Caching”.AcdpError::KeyResolutionUnreachable(transient) — absent from an older cached copy whose forced re-fetch the rate limit refused. No fresh document was seen, so absence is not proven.
Sourcepub fn invalidate(&self, did: &str)
pub fn invalidate(&self, did: &str)
Invalidate a specific DID’s cache entry, forcing a fresh fetch.