TypeScript: @dnsid-ai/transport
Node.js transport implementation for DNSid TypeScript packages.
Use this package when you want default Node DNS and HTTPS/TLS plumbing for @dnsid-ai/protocol or @dnsid-ai/sdk/node.
Responsibilities
Section titled “Responsibilities”- DNS TXT resolution through system DNS or a configured DNS server
- DNS server parsing and lookup helpers
- HTTPS JSON fetching
- SSRF-safe
fetchfor profile packages that need connection-time DNS checks - TLS certificate capture
- custom CA bundle support
- custom DNS server support
This package is Node-only and intentionally separate from @dnsid-ai/protocol.
Install
Section titled “Install”npm install @dnsid-ai/transportExample
Section titled “Example”import { createDefaultDnsResolver, createSsrfSafeFetch, fetchJson } from '@dnsid-ai/transport';
const dnsResolver = createDefaultDnsResolver({ dnsServer: process.env.DNSID_DNS_SERVER });const result = await fetchJson('https://agent.example/.well-known/dnsid-status.json', { allowedHost: 'agent.example', dnsServer: process.env.DNSID_DNS_SERVER, caBundlePath: process.env.DNSID_CA_BUNDLE,});
const safeFetch = createSsrfSafeFetch({ dnsServer: process.env.DNSID_DNS_SERVER });Most Node applications should use this through @dnsid-ai/sdk/node or profile defaults.
The default resolver queries the DNS servers returned by Node’s dns.getServers() in order, using UDP with TCP fallback to capture the recursive server’s remaining TXT TTL (capped at one day). dnsServer can override this list. On failures or empty answers it falls back to Node’s native TXT lookup with TTL 0, disabling identity caching for that result. Node’s server list may omit macOS scoped/split-DNS routing, so even positive wire answers are not guaranteed to match the platform resolver in those setups. Neither path validates DNSSEC (UNKNOWN); SERVFAIL is a resolution error. validated/required DNSSEC modes still need an injected validating resolver. Cached verification still re-fetches status unless verification.statusCheckInterval is set.
createSsrfSafeFetch() enforces unsafe-address rejection in the lookup used by the outgoing socket and returns redirects without following them automatically. Trusted test/private deployments may pass privateAddressHosts: exact hostnames or leading-dot suffixes such as .test (label-bounded, case-insensitive); only RFC 1918/ULA private and loopback results are then accepted for matching hosts, while link-local, mixed public/private, and IP-literal URLs remain blocked. Nothing is allowed by default, not even .test; a local dnsid stack needs privateAddressHosts: ['.test'] (or DNSID_PRIVATE_HOSTS=.test through loadEnvironment). Profile packages that accept a custom fetch cannot force equivalent behavior on arbitrary implementations, so custom fetch injection remains trusted infrastructure.
Node.js DNS and HTTPS transport for the DNSid protocol.
Provides the concrete network layer consumed by @dnsid-ai/core:
DNS TXT resolvers for fetching identity records, SSRF-safe HTTPS JSON fetching
that captures the peer TLS certificate, and fetch factories that route
requests through a custom DNS server and/or CA bundle. Built on Node
built-ins (node:dns, node:https, node:tls, node:net) plus undici;
this package is Node-only and not usable in browsers.
IMPORTANT limitation: these resolvers cannot determine DNSSEC validation
state. TXT lookups report DNSSECState.UNKNOWN; this package is not a
production DNSSEC validator. DNS-over-HTTPS (DoH) is not supported.
Interfaces
Section titled “Interfaces”FetchResult
Section titled “FetchResult”Defined in: transport/src/index.ts:33
Result of fetchJson: the parsed body plus the TLS certificate presented by the peer.
Properties
Section titled “Properties”data: unknown;Defined in: transport/src/index.ts:35
JSON-decoded response body.
tlsCert
Section titled “tlsCert”tlsCert: TLSCertificate;Defined in: transport/src/index.ts:37
Peer certificate details (expiry and DNS SANs) captured from the TLS session.
HTTPSFetchOptions
Section titled “HTTPSFetchOptions”Defined in: transport/src/index.ts:41
Options controlling fetchJson.
Properties
Section titled “Properties”allowedHost?
Section titled “allowedHost?”optional allowedHost?: string;Defined in: transport/src/index.ts:45
If set, the URL host (and every redirect host) must match this host exactly.
caBundlePath?
Section titled “caBundlePath?”optional caBundlePath?: string;Defined in: transport/src/index.ts:51
Path to a PEM CA bundle appended to the system root certificates.
dnsServer?
Section titled “dnsServer?”optional dnsServer?: string;Defined in: transport/src/index.ts:53
Custom DNS server (host, host:port, or [ipv6]:port) used to resolve the target.
domainBoundary?
Section titled “domainBoundary?”optional domainBoundary?: boolean;Defined in: transport/src/index.ts:47
With allowedHost, also accept subdomains of the allowed host (*.allowedHost).
maxResponseBytes?
Section titled “maxResponseBytes?”optional maxResponseBytes?: number;Defined in: transport/src/index.ts:55
Maximum accepted response body size in bytes. Defaults to 1 MiB.
privateAddressHosts?
Section titled “privateAddressHosts?”optional privateAddressHosts?: readonly string[];Defined in: transport/src/index.ts:57
Same as TransportConfig.privateAddressHosts: hostnames or .suffix entries that may resolve to loopback/private addresses.
signal?
Section titled “signal?”optional signal?: AbortSignal;Defined in: transport/src/index.ts:43
Shared verification deadline/cancellation, including redirects.
timeoutMs?
Section titled “timeoutMs?”optional timeoutMs?: number;Defined in: transport/src/index.ts:49
Request timeout in milliseconds. Defaults to 10 000.
SsrfSafeFetchOptions
Section titled “SsrfSafeFetchOptions”Defined in: transport/src/index.ts:71
Explicit exceptions for trusted test/private deployments using createSsrfSafeFetch.
Properties
Section titled “Properties”privateAddressHosts?
Section titled “privateAddressHosts?”optional privateAddressHosts?: readonly string[];Defined in: transport/src/index.ts:78
Hostnames, or leading-dot suffixes such as .test, whose resolved RFC 1918/ULA private or
loopback addresses may be contacted. Link-local and other unsafe ranges remain blocked, IP-literal
URLs are never exempted, and nothing is allowed by default. Same semantics as
TransportConfig.privateAddressHosts.
TransportConfig
Section titled “TransportConfig”Defined in: protocol/src/types.ts:105
SDK-managed DNS and HTTPS deployment settings (DnsidConfig.transport). Never alters protocol semantics.
Properties
Section titled “Properties”caBundlePath?
Section titled “caBundlePath?”optional caBundlePath?: string;Defined in: protocol/src/types.ts:109
Path to a PEM CA bundle appended to the system root certificates for TLS verification.
dnsServer?
Section titled “dnsServer?”optional dnsServer?: string;Defined in: protocol/src/types.ts:107
Custom DNS server (host, host:port, or [ipv6]:port). Omit to use the system resolver.
privateAddressHosts?
Section titled “privateAddressHosts?”optional privateAddressHosts?: readonly string[];Defined in: protocol/src/types.ts:121
Hostnames, or leading-dot suffixes such as .test, whose SDK-managed HTTPS destinations may
resolve to loopback or private-use addresses. An exact entry matches only that name; .test
matches test and every name beneath it on a DNS-label boundary, case-insensitively and
ignoring a trailing dot. Link-local, multicast, reserved, and mixed public/private resolutions
stay rejected, IP-literal URLs are never exempted, and every redirect hop is matched
independently. Nothing is allowed by default; there is no built-in .test exemption. Entries
are validated at construction (IP literals, ports, schemes, paths, credentials → ArgumentError).
Applies only to the default fetcher; rejected when fetchJson is injected. Not a DNSid protocol
field.
Type Aliases
Section titled “Type Aliases”FetchLike
Section titled “FetchLike”type FetchLike = (input, init?) => Promise<Response>;Defined in: transport/src/index.ts:30
Minimal WHATWG-fetch-compatible function signature returned by the fetch factories.
Parameters
Section titled “Parameters”RequestInfo | URL
RequestInit
Returns
Section titled “Returns”Promise<Response>
Functions
Section titled “Functions”createDefaultDnsResolver()
Section titled “createDefaultDnsResolver()”function createDefaultDnsResolver(config): DNSResolver;Defined in: transport/src/index.ts:165
Creates the DNS resolver used to fetch DNSid identity records (TXT).
Uses the configured server, or queries Node’s system DNS server list in order.
Wire replies supply remaining TXT TTLs; if those servers cannot answer, the
system TXT API is the fallback (TTL 0). DNSSEC state is always UNKNOWN.
Parameters
Section titled “Parameters”config
Section titled “config”Pick<TransportConfig, "dnsServer">
Returns
Section titled “Returns”createDnsidFetch()
Section titled “createDnsidFetch()”function createDnsidFetch(config): FetchLike;Defined in: transport/src/index.ts:105
Creates a fetch function honoring the transport configuration.
With no custom DNS server or CA bundle this returns the global fetch
unchanged. Otherwise it returns an undici-backed fetch whose connections
resolve hostnames via the configured DNS server and/or trust the extra CA
bundle in addition to the system roots. Unlike createSsrfSafeFetch,
the returned fetch applies no private-address filtering.
Parameters
Section titled “Parameters”config
Section titled “config”DNS server and/or CA bundle overrides.
Returns
Section titled “Returns”A FetchLike suitable for passing to core verification APIs.
Example
Section titled “Example”import { createDnsidFetch, createDefaultDnsResolver } from '@dnsid-ai/transport';
const config = { dnsServer: '1.1.1.1' };const fetchImpl = createDnsidFetch(config);const dnsResolver = createDefaultDnsResolver(config);
const res = await fetchImpl('https://agent.example/.well-known/jwks.json');const [records, dnssec] = await dnsResolver.fetchTXT('_dnsid.agent.example');createDnsResolverFromServer()
Section titled “createDnsResolverFromServer()”function createDnsResolverFromServer(server): DNSResolver;Defined in: transport/src/index.ts:198
Creates a DNSResolver that queries a specific DNS server.
Accepts host, host:port, or [ipv6]:port. A hostname (rather than an
IP literal) is resolved once via the system resolver on first use, then
cached for the resolver’s lifetime. TXT answers carry the remaining TTL
(capped at one day) and DNSSECState.UNKNOWN; NXDOMAIN/NODATA yield an empty
record set, and other errors (including SERVFAIL) reject.
Parameters
Section titled “Parameters”server
Section titled “server”string
DNS server address, optionally with port.
Returns
Section titled “Returns”createLookup()
Section titled “createLookup()”function createLookup(dnsServer): LookupFunction;Defined in: transport/src/index.ts:267
Creates a Node lookup function that resolves A/AAAA records via the given
DNS server instead of the system resolver.
A hostname-form server is itself resolved once via the system resolver and
cached. Suitable for https.RequestOptions.lookup or an undici connect
option. The callback receives an Error when no A/AAAA records exist.
Parameters
Section titled “Parameters”dnsServer
Section titled “dnsServer”string
DNS server address (host, host:port, or [ipv6]:port).
Returns
Section titled “Returns”LookupFunction
createSsrfSafeFetch()
Section titled “createSsrfSafeFetch()”function createSsrfSafeFetch(config?, options?): FetchLike;Defined in: transport/src/index.ts:132
Creates a fetch function that rejects connections to private, loopback, link-local, and other non-routable addresses (SSRF protection).
Every hostname is resolved (via the configured DNS server, or the system resolver otherwise) and each resulting address is checked with isUnsafeIp before connecting; requests resolving to an unsafe address fail with a VerificationError (TLSError, surfaced through the fetch rejection). Redirects are returned to the caller instead of followed automatically so each destination can be explicitly policy-checked.
Parameters
Section titled “Parameters”config?
Section titled “config?”TransportConfig = {}
Optional DNS server and/or CA bundle overrides.
options?
Section titled “options?”SsrfSafeFetchOptions = {}
Explicit hostname-scoped private-address exceptions for trusted test/private deployments.
Returns
Section titled “Returns”A FetchLike with address filtering applied on every lookup.
fetchJson()
Section titled “fetchJson()”function fetchJson(url, opts?): Promise<FetchResult>;Defined in: transport/src/index.ts:230
Fetches a JSON document over HTTPS with strict transport checks, returning the parsed body together with the peer TLS certificate.
Enforces: HTTPS-only URLs, optional host pinning via allowedHost (with
optional subdomain boundary), SSRF-safe address resolution, at most 5
same-policy HTTPS redirects, an expected 200 status, and a bounded response
body size. All lookups route through opts.dnsServer when provided.
Parameters
Section titled “Parameters”string
Absolute HTTPS URL to fetch.
Transport constraints; see HTTPSFetchOptions.
Returns
Section titled “Returns”Promise<FetchResult>
The parsed JSON body and captured TLS certificate.
Throws
Section titled “Throws”VerificationError with VerificationCode.TLSError for policy or
transport failures (non-HTTPS URL, disallowed or unsafe host, redirect
violations, non-200 status, oversized body, timeout, TLS/connection
errors); with VerificationCode.RecordInvalid when the body is not valid
JSON. Network-shaped failures are marked transient.
formatDnsServer()
Section titled “formatDnsServer()”function formatDnsServer(address, port?): string;Defined in: transport/src/index.ts:252
Formats a resolved address (bracketing IPv6) with an optional port for Resolver.setServers.
Parameters
Section titled “Parameters”address
Section titled “address”string
string
Returns
Section titled “Returns”string
isUnsafeIp()
Section titled “isUnsafeIp()”function isUnsafeIp(address): boolean;Defined in: transport/src/index.ts:396
True if an IP address must not be contacted by SSRF-safe transports: private, loopback, link-local, CGN, documentation, multicast, reserved, 6to4/Teredo, and IPv4-mapped/compatible IPv6 forms of any of these. Malformed IP-shaped input is treated as unsafe; non-IP strings return false.
Parameters
Section titled “Parameters”address
Section titled “address”string
Returns
Section titled “Returns”boolean
parseDnsServer()
Section titled “parseDnsServer()”function parseDnsServer(server): | { host: string; port?: string;} | null;Defined in: transport/src/index.ts:241
Splits a DNS server string into host and optional port.
Supports host, host:port, and [ipv6]:port. Unbracketed strings
containing multiple colons (bare IPv6 literals) are returned whole as
host, which callers pass through to Node unchanged.
Parameters
Section titled “Parameters”server
Section titled “server”string
Returns
Section titled “Returns”| {
host: string;
port?: string;
}
| null