Skip to main content

Crate aph_resolver

Crate aph_resolver 

Source
Expand description

Ready-made I/O adapters for APH notary-key discovery (spec §8.4).

aph-core parses and never fetches. Two of §8.4’s three publication mechanisms are defined in terms of I/O — a DNS TXT query (§8.4.5) and an HTTPS GET of a DID Document (§8.4.4) — and aph-core declares them as two narrow one-method ports so the fetching lives outside it. This crate is one implementation of those two ports, carrying the DNS and HTTP dependencies that aph-core deliberately does not.

§Who this is for

Adopters with no adapter layer of their own. If you are writing a verifier — a CLI, a service, a test harness — and you have no opinion about how HTTP and DNS should be performed, resolve gets you a notary key in one call and the guards below come with it.

It is explicitly not for a host that already has a transport stack. An OS or platform with its own HTTP client, its own DNS policy, its own timeout and retry budget and its own audit log should implement aph_core::discovery::ports over THAT stack, which is a few dozen lines, and keep one place where outbound requests are made. That is exactly what Squillo does, and it is why Squillo never links this crate: two HTTP clients in one process means two timeout policies, two proxy configurations and two answers to “what did this machine connect to”.

§What this crate does NOT decide

Every parsing, selection and ordering rule stays in aph-core: tag-list parsing and key selection (discovery::dns_txt), DID Document parsing and verification-method lookup (discovery::did_document), and the §8.4.6 mechanism order with its no-downgrade rule (discovery::composer). This crate contributes bytes and one classification — see below — and forms no opinion about what those bytes mean. A second parser here would be a second chance to disagree with the specification.

The one judgement it does make is the ABSENCE/FAILURE split, and it is made because only the adapter can see it. §8.4.6 advances the mechanism sequence on absence and refuses on failure, so a DNS answer of NXDOMAIN (or NOERROR with no TXT records) becomes Ok(aph_core::discovery::DiscoveryOutcome::Absent) while a timeout, SERVFAIL or REFUSED becomes APH_E008. That distinction is what keeps a notary publishing only a DID Document verifiable, without letting whoever can block DNS choose which anchor a verifier trusts.

§Security posture — the five SSRF controls

The host a verifier connects to is named by the envelope being verified, which means it is chosen by an untrusted party. ReqwestDocumentFetch therefore applies five controls: a deny table over EVERY resolved address before any connect with the validated set pinned so the connect cannot re-resolve; HTTPS only with no certificate-validation escape hatch; redirects refused and the response origin re-checked; a 5 s budget and a 64 KiB body cap enforced while streaming; and ONE opaque APH_E008 outward for every failure, so a verifier cannot be used as a network probe by whoever chose the DID. The source states each control at the line that implements it.

§Example

// The DID URL comes from the envelope's `proof.verificationMethod`, and
// the instant comes from its `decisionTimestamp` — not the wall clock,
// so an envelope signed before a rotation keeps verifying.
let key = aph_resolver::resolve(
  "did:web:notary.example.com#k1",
  "2026-06-01T00:00:00Z",
)
.await?;
std::println!("{} byte key", key.key_bytes.len());

Structs§

HickoryTxtLookup
A aph_core::discovery::ports::TxtRecordLookup backed by a system- configured hickory resolver.
ReqwestDocumentFetch
A aph_core::discovery::ports::DidDocumentFetch over reqwest, with all five SSRF controls applied.

Functions§

resolve
Resolves the notary public key a DID URL names, building both adapters for you (spec §8.4.6).