Network
Framework network helpers — DNS-over-HTTPS dispatch, TLS configuration, OCSP/CT validation, NTP/NTS-KE bootstrap.
b.network is the umbrella facade over the framework's outbound- network surface: DNS (default DoH on, optional DoT, lookup cache with TTL bound), TLS trust store (CA bundle / system trust / ignored-cert opt-in), proxy resolution from HTTP_PROXY / HTTPS_PROXY / NO_PROXY, NTP / NTS-KE drift checks, SMTP policy (MTA-STS / DANE / TLS-RPT), heartbeat watchdog, byte quota, SSRF allowlist, and socket-level defaults (TCP_NODELAY / SO_KEEPALIVE).
bootFromEnv reads BLAMEJS_* environment variables once at startup and applies the union to the live facade — operators wire it from a process-supervisor's env without touching code. snapshot returns a redacted view of the current configuration for the operations dashboard. applyToSocket is the per-socket tuning hook for primitives building their own server (tls, wsServer, etc.).
b.network.dns.dane.matchCertificate(opts) #
{
{
tlsa: [ { usage, selector, matchingType, data: Buffer|hex } ], // the TLSA RRset
certificate: Buffer, // leaf certificate (DER)
chain?: Buffer[], // intermediate / CA certs (DER), for TA usages
}
}
Match a server certificate against a set of (DNSSEC-verified) TLSA records (RFC 6698 / 7671). For each record the selected data — the full certificate DER (selector 0) or its subjectPublicKeyInfo (selector 1) — is hashed per the matching type (exact / SHA-256 / SHA-512) and compared, constant-time, to the record's association data. End-entity usages (PKIX-EE 1, DANE-EE 3) are matched against the leaf certificate; trust-anchor usages (PKIX-TA 0, DANE-TA 2) are matched against the leaf and any supplied chain.
Returns the matching record plus what the caller must still do: a DANE-EE match is self-sufficient (the TLSA pins the key); a DANE-TA match still needs chain-to-anchor verification; PKIX usages still need full PKIX validation. Throws dane/no-match if nothing matches. Verify the TLSA RRset with b.network.dns.dnssec before trusting the records — an unauthenticated TLSA proves nothing.
var r = b.network.dns.dane.matchCertificate({ tlsa: records, certificate: leafDer });
// → { ok: true, matched: { usage: 3, selector: 1, matchingType: 1 }, daneAuthenticated: true, pkixRequired: false }
b.network.dns.resolver.create(opts?) #
{
profile: "strict" | "balanced" | "permissive",
posture: "hipaa" | "pci-dss" | "gdpr" | "soc2",
maxTtlMs: number, // cap any TTL from upstream; default 24h
minTtlMs: number, // floor short-TTL records; default 60s
serveStale: number | false, // ms to retain expired entries; default 6h
timeoutMs: number, // per-query upstream deadline; default 10s
transport: { lookup(name, qtype) → Promise }, // operator override
audit: b.audit namespace,
}
Build a resolver instance with the given options. Returns an instance with .query(name, type, opts) → Promise<{ rrs, ttl, fromCache, stale, validated, response }> plus per-type shortcuts.
var resolver = b.network.dns.resolver.create({ profile: "strict" });
var r = await resolver.queryTxt("_dmarc.example.com");
console.log(r.rrs.map(function (rr) { return rr.decoded.join(""); }));
b.network.dns.resolver.followCnames(name, type, opts?) #
{
validate: boolean, // per-call: refuse if upstream AD=0
}
Walk CNAME redirections until the target record arrives or the chain depth cap from b.safeDns trips. Returns the same shape as query() plus chain: [name, name, ...] listing each hop.
var r = await resolver.followCnames("alias.example.com", "A");
console.log(r.chain, r.rrs.map(function (rr) { return rr.decoded; }));
b.network.dns.resolver.resolveTxt(qname, dnsLookup?, resolver?) #
Resolve TXT records for qname via the operator's dnsLookup(qname, "TXT") override when supplied, otherwise the framework's shared validating (DoH / DoT / system, DNSSEC-checked) resolver — normalized to the string[][] shape dnsPromises.resolveTxt returns. Throws an ENODATA Error when no TXT records exist.
This is the secure DNS path for every email-auth policy lookup (DMARC / DKIM / ARC / BIMI / MTA-STS / TLS-RPT). Plaintext system DNS (dnsPromises.resolveTxt) is spoofable — DNS cache-poisoning / Kaminsky-class — and must NOT be used for records a downgrade or redirect would exploit.
Pass resolver (a b.network.dns.resolver.create() instance) to reshape TXT records off a caller-owned resolver instead of the shared one — the mail-auth and mail-dkim modules use this so their TXT lookups share the same resolver (and cache) they use for A / MX / PTR, instead of each re-rolling the reshape.
var rrs = await b.network.dns.resolver.resolveTxt("_dmarc.example.com");
// → [ ["v=DMARC1; p=reject"] ]
b.network.dns.resolver.safeResolveTxt(qname, opts?) #
{
dnsLookup: function, // operator override (qname, "TXT") => string[][]
errorFactory: function, // (code, message) => Error, thrown on non-absence failures
code: string, // error code passed to errorFactory
}
resolveTxt wrapped with the email-auth policy-fetch convention: a domain with no record (ENOTFOUND / ENODATA) returns null (absence is not an error); any other failure throws the caller's typed error via errorFactory(code, message) so each protocol keeps its own error class. The same secure resolver path as resolveTxt — never plaintext system DNS.
var rrs = await b.network.dns.resolver.safeResolveTxt("_mta-sts.example.com", {
errorFactory: function (code, msg) { return new SmtpPolicyError(code, msg); },
code: "smtp/mta-sts-txt-lookup-failed",
});
if (rrs === null) return null; // no MTA-STS record published
b.network.dns.querySvcb(name, opts?) #
{
{
transport: "doh" | "dot" | "system",
}
}
Query SVCB records (RFC 9460 §2) for name. Returns an array of { priority, target, params } records sorted by priority. AliasMode records (priority === 0) carry a target and empty params — the caller chases the alias by re-querying the target. ServiceMode records (priority > 0) carry SvcParams: alpn / port / ipv4hint / ipv6hint / ech / mandatory / dohpath. Unknown SvcParamKeys surface under params.unknown[key] as raw bytes — operators implementing forward-compat can still read them. Malformed rdata throws DnsError with code dns/svcb-malformed.
var b = require("@blamejs/core");
var rrs = await b.network.dns.querySvcb("_443._wss.example.com");
b.network.dns.queryHttps(name, opts?) #
{
{
transport: "doh" | "dot" | "system",
}
}
Query HTTPS records (RFC 9460 §9). Identical to querySvcb except the QTYPE is HTTPS (65) — the user-agent-facing variant of SVCB for https:// origins. Browsers query this for ECH discovery and h3 advertisement; servers can call it to validate their own published HTTPS RRset. Returns the same shape as querySvcb.
var b = require("@blamejs/core");
var rrs = await b.network.dns.queryHttps("example.com");
b.network.dns.discoverEncrypted(opts?) #
{
{
name: string,
insecureSystemResolverOnly: boolean,
}
}
RFC 9462 Discovery of Designated Resolvers. Queries _dns.resolver.arpa for SVCB records that advertise encrypted DNS alternatives (DoH / DoT) hosted by the network's currently-configured Do53 resolver. Returns a list of resolver descriptors with { transport, alpn, target, port, dohpath, ipv4hint, ipv6hint, priority }.
The discovery query goes through the system resolver by default (RFC 9462 §4 — DDR validation requires the response to come from the Do53 resolver whose IP we compare). Callers that already have a trusted DoH / DoT transport configured can pass { insecureSystemResolverOnly: false } to allow DDR via the encrypted transport too.
Throws DnsError with code dns/ddr-not-discovered when the resolver does not publish DDR records.
var b = require("@blamejs/core");
var resolvers = await b.network.dns.discoverEncrypted();
b.network.dns.useDesignatedResolvers(list) #
RFC 9463 Discovery of Network-designated Resolvers. The framework doesn't run a DHCP / IPv6 RA client itself; an operator-side agent (or the output of discoverEncrypted()) supplies the resolver list and the framework swaps its transport over to the lowest-priority entry. Items are tried in order: the first one that successfully configures (DoH useDnsOverHttps, DoT useDnsOverTls) wins.
Each entry shape:
{ transport: "doh" | "dot", url: string, host: string, port: number, servername: string, alpn: Array
Throws DnsError with code dns/dnr-no-resolvers if list is empty, and dns/dnr-malformed if an entry is missing its required transport-specific fields.
var b = require("@blamejs/core");
var found = await b.network.dns.discoverEncrypted();
b.network.dns.useDesignatedResolvers(found.map(function (r) {
return r.transport === "doh"
? { transport: "doh", url: "https://" + r.target + (r.dohpath || "/dns-query") }
: { transport: "dot", host: r.target, port: r.port, servername: r.target };
}));
b.network.dns.isNullMx(mxRecords) #
RFC 7505 Null-MX check — returns true when the supplied MX records signal "this domain does not accept email" (a single MX record with priority 0 and exchange .). Operators sending mail call this before delivery to skip domains that have explicitly opted out of email. Returns false for any other shape (zero records, multiple records, non-zero priority, non-. exchange).
MX records are expected in the { priority, exchange } shape returned by node:dns.resolveMx (or b.network.dns.resolve(host, "MX")). Operator supplies the records; this is a pure classifier, no network call.
var node = require("node:dns/promises");
var mx;
try { mx = await node.resolveMx("example.com"); }
catch (e) { mx = []; }
if (b.network.dns.isNullMx(mx)) {
throw new Error("example.com publishes Null-MX (RFC 7505) — does not accept email");
}
b.network.dns.classifyDnskeyAlgorithm(algorithm) #
Classify a DNSKEY / RRSIG algorithm number against the IANA DNS Security Algorithm Numbers registry, flagging SHA-1-based and other deprecated algorithms per RFC 9905 (Deprecating DNSSEC SHA-1 Usage), RFC 8624 (Algorithm Implementation Requirements), and RFC 6944 / RFC 6725 (RSAMD5 deprecation).
Returns { algorithm, name, deprecated, reason, known } for any IANA-assigned number; known: false for unassigned numbers (operators decide whether unassigned == deprecated for their threat model). Returns null for non-integer / non-finite input.
Operators auditing inbound DNSSEC chain-of-trust evidence call this on each link's algorithm number and refuse the validation when deprecated === true. Defensive request-shape reader — never throws.
var v = b.network.dns.classifyDnskeyAlgorithm(5);
// → { algorithm: 5, name: "RSASHA1", deprecated: true,
// reason: "SHA-1 deprecated (RFC 9905 §3)", known: true }
if (v && v.deprecated) throw new Error("refuse DNSSEC algo " + v.name);
b.network.dns.classifyDnskeyAlgorithm(13);
// → { algorithm: 13, name: "ECDSAP256SHA256", deprecated: false, ... }
b.network.dns.classifyDsDigestType(digestType) #
Classify a DS-record digest type against the IANA DNSSEC Delegation Signer (DS) Resource Record (RR) Type Digest Algorithms registry, flagging SHA-1 (digest type 1) as deprecated per RFC 9905 §4.
Returns { digestType, name, deprecated, reason, known } for any IANA-assigned number; null for non-integer input.
var v = b.network.dns.classifyDsDigestType(1);
// → { digestType: 1, name: "SHA-1", deprecated: true,
// reason: "SHA-1 deprecated (RFC 9905 §4)", known: true }
b.network.dns.classifyDsDigestType(2);
// → { digestType: 2, name: "SHA-256", deprecated: false, ... }
b.network.dns.dnssec.keyTag(dnskeyRdata) #
Compute the RFC 4034 Appendix B key tag of a DNSKEY from its full RDATA (flags || protocol || algorithm || public key) — the 16-bit identifier an RRSIG / DS references to select the signing key.
var tag = b.network.dns.dnssec.keyTag(dnskeyRdata);
b.network.dns.dnssec.verifyDs(opts) #
{
{
ownerName: string, // the child zone name (the DNSKEY owner)
dnskeyRdata: Buffer, // full DNSKEY RDATA (flags||protocol||alg||publicKey)
ds: { keyTag, algorithm, digestType, digest: Buffer }, // the parent DS
}
}
Verify a DS (Delegation Signer) record against a child DNSKEY — the link that lets a parent zone vouch for a child's key. The DS digest (SHA-256 / SHA-384) is recomputed over the owner name plus the DNSKEY RDATA and compared to the DS, with the key tag and algorithm checked.
b.network.dns.dnssec.verifyDs({ ownerName: "example.com", dnskeyRdata: ksk, ds: parentDs });
b.network.dns.dnssec.verifyRrset(opts) #
{
{
name: string, // owner name of the RRset
type: string|number, // RR type (e.g. "DNSKEY", "A")
class?: number, // default 1 (IN)
rdatas: Buffer[], // each record's wire-format RDATA
rrsig: { // the RRSIG covering the RRset
algorithm, labels, originalTtl, expiration, inception, keyTag,
signerName: string, signature: Buffer,
},
dnskey: { algorithm, publicKey: Buffer }, // the signing DNSKEY (publicKey = bytes after flags/proto/alg)
at?: Date, // validity instant (default now); must be a valid Date
}
}
Verify an RRSIG over an RRset against a DNSKEY (RFC 4035 §5.3). The signed data is reconstructed in canonical form — the RRSIG RDATA without the signature, then the RRset's records ordered by canonical RDATA with the RRSIG Original TTL — and the signature is verified with the DNSKEY (RSA/SHA-256, ECDSA P-256/384, Ed25519). The signature's inception / expiration window is enforced against opts.at. RR types carrying embedded domain names are refused (dnssec/uncanonicalizable-type) rather than mis-validated.
b.network.dns.dnssec.verifyRrset({ name: "example.com", type: "DNSKEY", rdatas: keys, rrsig: sig, dnskey: ksk });
b.network.dns.dnssec.nsec3Hash(name, opts) #
{
{
salt: Buffer, // zone NSEC3 salt (may be empty)
iterations: number, // additional hash iterations (>= 0)
}
}
Compute the RFC 5155 §5 NSEC3 hash of a name — iterated SHA-1 over the canonical (lowercased, root-terminated) wire form with the zone's salt. The result is the unencoded hash; the NSEC3 owner label is its base32hex encoding. SHA-1 is the only hash IANA registers for NSEC3, so this is a wire-protocol constant, not a cryptographic default.
var h = b.network.dns.dnssec.nsec3Hash("a.example.com", { salt: salt, iterations: 0 });
b.network.dns.dnssec.verifyDenial(opts) #
{
{
qname: string, // the queried name
qtype: string|number, // queried type (required for proof "nodata")
proof: string, // "nxdomain" | "nodata"
zone: string, // the signer zone apex (a suffix of qname)
nsec3?: [ { owner: string, rdata: Buffer } ], // NSEC3 records (owner = base32hex-label.zone)
nsec?: [ { owner: string, rdata: Buffer } ], // NSEC records
maxIterations?: number, // NSEC3 iteration cap (default 500)
allowOptOut?: boolean, // accept an Opt-Out NXDOMAIN proof (default false)
}
}
Prove that a name does not exist (NXDOMAIN) or that a name has no records of a given type (NODATA) from the signed NSEC (RFC 4034 §4) or NSEC3 (RFC 5155) records in a response's Authority section. This is the other half of "verify the answer yourself": verifyRrset proves a positive answer, verifyDenial proves a negative.
The records MUST already be verified with verifyRrset — this checks the denial RELATION (closest-encloser, covering ranges, type-bitmap absence), not the signatures. For NSEC3, the iterated-hash count is capped (opts.maxIterations, default 500) to bound the SHA-1 work an attacker can force. An NXDOMAIN proof that relies on an Opt-Out NSEC3 (RFC 5155 §6) is refused unless opts.allowOptOut — opt-out only proves "no signed records", not non-existence.
b.network.dns.dnssec.verifyDenial({
qname: "nope.example.com", proof: "nxdomain", zone: "example.com", nsec3: records,
});
b.network.dns.dnssec.verifyChain(opts) #
{
{
links: [ { // ordered root-first
zone: string,
dnskeys: Buffer[], // the zone's DNSKEY RRset RDATAs
dnskeyRrsig: { algorithm, labels, originalTtl, expiration, inception, keyTag, signerName, signature },
dsRdatas?: Buffer[], // DS RRset for this zone (served by parent; omit for root)
dsRrsig?: { ... }, // RRSIG over the DS RRset (signed by parent; omit for root)
} ],
trustAnchors?: [ { keyTag, algorithm, digestType, digest: Buffer } ], // default IANA root
at?: Date, // validity instant (default now)
}
}
Validate a DNSSEC delegation chain from the root down to a zone, against a pinned trust anchor (RFC 4035 §5). For each link, the zone's DNSKEY RRset must be self-signed by one of its keys; that signing key must be vouched for either by a pinned anchor (root) or by a DS record served by the already-trusted parent. The DS RRset itself is verified against the parent's keys, so trust flows root → TLD → zone with no gap. The default anchors are the IANA root KSKs; override with opts.trustAnchors.
This composes verifyRrset + verifyDs + the key tag; it returns the leaf zone's trusted DNSKEY set, which the caller then passes to verifyRrset / verifyDenial for the actual answer.
KeyTrap (CVE-2023-50387) amplification is bounded with non-configurable caps: at most 4 same-tag DNSKEY candidates are tried per RRSIG, at most 64 DNSKEYs per zone link and 16 DS records per delegation are accepted, the chain is at most 128 links deep, and the whole response is held to a signature-validation budget that scales with chain depth (so a legitimate deep delegation always fits while bounded collisions stay bounded). A hostile zone publishing many colliding keys / signatures is refused with dnssec/too-many-colliding-keys / dnssec/too-many-dnskeys / dnssec/too-many-ds / dnssec/too-many-links / dnssec/validation-budget-exceeded rather than driving O(keys x sigs) verifications. (NSEC3 iteration counts are separately capped at 150 per RFC 9276 / the CVE-2023-50868 fix.)
var trusted = b.network.dns.dnssec.verifyChain({ links: [rootLink, orgLink] });
// → { ok: true, zone: "org.", keys: [ ...trusted org DNSKEY rdatas ] }
b.network.tls.certificateCompressionAlgorithms() #
The RFC 8879 certificate-compression algorithms this runtime can decompress, newest-runtime order, as a fresh array you own.
Certificate compression shrinks the TLS Certificate message — on a post-quantum deployment the largest thing on the wire during a handshake, since an ML-DSA-87 signature alone runs about 4.6 KB before any public key or chain. Every outbound path the framework ships already advertises this list, and b.router advertises it inbound; call this directly only when you are assembling tls.connect options yourself and want to narrow the list or check what the runtime supports.
Compressing the certificate is not the record-layer compression that CRIME attacked: the Certificate message is public, fixed, and not attacker-influenced, so its compressed length reveals nothing about a secret and no attacker-chosen plaintext shares a compression context with one.
Returns an empty array on a runtime that does not implement the extension, which reads naturally as "advertise nothing".
b.network.tls.certificateCompressionAlgorithms();
// -> ["zlib", "brotli", "zstd"]
// Narrow what an outbound connection will accept.
var opts = b.network.tls.buildOptions({ certificateCompression: ["brotli"] });
b.network.tls.outboundPosture() #
The framework's outbound TLS posture as a fresh options object, ready to merge into any tls.connect, https.request or https.Agent options:
var connectOpts = Object.assign({ host: h, port: p }, b.network.tls.outboundPosture());
Every protocol client the framework ships — DNS-over-HTTPS and DNS-over-TLS, NTS-KE, Redis, syslog, WebSocket, proxy tunnels, the HTTP client, the ECH and OCSP paths — merges this rather than listing the keys itself, so raising the posture is one edit that cannot reach some outbound paths and miss others.
It reads the LIVE key-share preference, so b.network.tls.preferredGroups.set(...) (or the b.network.tls.pqc alias) takes effect on the next dial across every one of those clients. Call it per connection rather than caching the result, or an operator's later narrowing will not reach the wire.
// Restrict every outbound handshake to the NIST-curve hybrids.
b.network.tls.preferredGroups.set(["SecP256r1MLKEM768", "SecP384r1MLKEM1024"]);
b.network.tls.outboundPosture();
// → { minVersion: "TLSv1.3",
// ecdhCurve: "SecP256r1MLKEM768:SecP384r1MLKEM1024",
// certificateCompression: ["zlib", "brotli", "zstd"] }
b.network.tls.parseEchConfigList(raw) #
Parse a draft-ietf-tls-esni-22 ECHConfigList byte string (the value of the ech= SvcParam in an SVCB or HTTPS DNS record per RFC 9460 paragraph 7.4.2). Accepts a Buffer or a strict-base64 string. Returns { rawLength, configs: [{ version, length, keyConfig, ... }] }.
For each ECHConfig at the published draft-22 version (0xfe0d) the decoded keyConfig carries configId, kemId, publicKey (Buffer), and cipherSuites (each { kdfId, aeadId }); the entry also exposes maximumNameLength, publicName, and extensions. Unknown future ECH versions surface their raw body Buffer so the caller can forward them to a Node build that supports them.
Throws NetworkTlsError("tls/ech-config-malformed") on any framing violation (truncated length prefix, vector overflow, bad cipher_suites stride, etc.).
var b = require("@blamejs/core");
var rrs = await b.network.dns.queryHttps("example.com");
var rec = rrs.find(function (r) { return r.params && r.params.ech; });
var parsed = b.network.tls.parseEchConfigList(rec.params.ech);
// parsed.configs[0].keyConfig.kemId === 0x0020 (X25519)
b.network.tls.connectWithEch(opts) #
{
{
host: string,
port: number,
alpn: string[],
ipFamily: 4 | 6,
timeoutMs: number,
servername: string,
ca: string|Buffer|Array,
checkServerIdentity: function,
echOverride: Buffer|string,
rejectUnauthorized: boolean,
}
}
b.network.tls.checkServerIdentity9525
Open a TLS-1.3 outbound connection with Encrypted Client Hello (ECH, draft-ietf-tls-esni-22) when the destination publishes an ech= SvcParam via SVCB/HTTPS records (RFC 9460 paragraph 2.4 / paragraph 9). The flow:
1. b.network.dns.queryHttps(host) to discover ECH config. 2. If any record carries ech=, the parsed ECHConfigList is attached to tls.connect({ ech }) so the outer ClientHello uses the published public_name SNI and the inner ClientHello (real SNI, ALPN, etc.) is HPKE-encrypted under the published public key. 3. If no record carries ech=, or DNS fails, the function falls back to a normal TLS connect (still TLSv1.3-floor + framework PQC group preference). Operators get an observability.event so the degradation is visible. 4. If the running Node build does not support the ech connect option, the function emits a one-shot warn and connects without ECH — never throws on missing Node-side support.
Returns the connected tls.TLSSocket once secureConnect fires. b.httpClient will compose this in a follow-up release; this primitive is the operator escape hatch for raw outbound TLS over ECH (custom protocol clients, mTLS testing, ECH validation tools).
var b = require("@blamejs/core");
var sock = await b.network.tls.connectWithEch({
host: "ech-target.example.com",
alpn: ["h2", "http/1.1"],
});
sock.write("GET / HTTP/1.1\r\nHost: ech-target.example.com\r\n\r\n");
b.network.tls.checkServerIdentity9525(host, cert) #
Drop-in replacement for Node's tls.checkServerIdentity that implements RFC 9525 paragraph 6 strictly. Operators pass it to tls.connect({ checkServerIdentity }) (or to any framework primitive that exposes pkixStrict: true).
Differences vs Node's default matcher:
- SAN-required when present is mandatory: a peer cert lacking subjectAltName refuses with tls/pkix-san-required (RFC 9525 paragraph 6.4.4 forbids Common Name fallback). - CN-only legacy certs surface a distinct tls/pkix-cn-fallback-refused code so audit logs distinguish "missing SAN" from "ancient CN-only cert still shipping". - Wildcard matching is restricted to the entire leftmost label. *.example.com matches foo.example.com but NOT foo.bar.example.com and NOT example.com. Partial wildcards like f*o.example.com and middle wildcards like foo.*.example.com refuse. - IP literals match iPAddress SAN entries only — never DNS entries, never wildcards. IPv6 comparison is byte-equal after canonicalization (zone-id stripped, :: expanded).
Returns Error | undefined — the Error shape Node expects; when undefined, the connection is permitted to proceed.
var tls = require("node:tls");
var b = require("@blamejs/core");
var sock = tls.connect({
host: "internal.example.com",
port: 443,
checkServerIdentity: b.network.tls.checkServerIdentity9525,
});
b.network.dns.tsig.sign(message, opts) #
{
keyName: string, // REQUIRED — the shared-key name
secret: string | Buffer, // REQUIRED — base64 string or raw bytes
algorithm: string, // default: "hmac-sha256"
fudge: number, // default: 300 (seconds)
time: number, // default: now (Unix seconds)
originalId: number, // default: the message's own ID
requestMac: Buffer, // when signing a response (§5.4.1)
error: number, // default: 0 (NOERROR)
otherData: Buffer, // default: empty
allowLegacy: boolean, // permit HMAC-MD5 / HMAC-SHA-1
}
Append a TSIG resource record to a DNS message (a Buffer of wire bytes) and return the signed wire Buffer. The MAC is the HMAC over the message plus the RFC 8945 §4.3.3 TSIG variables. Returns { wire, mac } — wire is the message with the TSIG RR appended and ARCOUNT incremented, and mac is the raw HMAC (keep it to verify the matching response).
var signed = b.network.dns.tsig.sign(queryWire, {
keyName: "update.key.", secret: "",
});
socket.send(signed.wire);
b.network.dns.tsig.verify(message, opts) #
{
keys: object, // { "": { secret, algorithm } }
keyName: string, // single-key form (with secret)
secret: string | Buffer, // single-key form
algorithm: string, // expected algorithm (single-key form)
now: number, // default: now (Unix seconds)
requestMac: Buffer, // when verifying a response (§5.4.1)
allowLegacy: boolean,
}
Verify the TSIG record on a DNS message: locate the trailing TSIG RR, recompute the HMAC over the RFC 8945 §4.3.3 digest, compare it in constant time, and check that now is within fudge seconds of timeSigned. Returns { valid, keyName, algorithm, timeSigned, fudge, error, macValid, timeValid, reason }; valid is true only when the MAC matches, the time window holds, and the embedded error is NOERROR. Never throws for an authentication failure — only for a malformed message or unknown key shape.
var r = b.network.dns.tsig.verify(received, {
keys: { "update.key.": { secret: "" } },
});
if (!r.valid) refuse(r.reason);
b.network.socket.applyToSocket(socket) #
Apply the framework's socket defaults (TCP_NODELAY, SO_KEEPALIVE + initial-delay) to a freshly-created net.Socket / tls.TLSSocket. Best-effort: a socket that has already errored, lacks the setter methods, or rejects the call is left as-is. Returns the same socket. Used by primitives that build their own server (b.tls, b.wsServer, b.smtp) so every socket on the wire follows the same tuning.
var net = require("net");
var s = new net.Socket();
var ret = b.network.socket.applyToSocket(s);
ret === s;
// → true
s.destroy();
b.network.bootFromEnv(opts) #
{
env: object, // default process.env — pass a fixture object in tests
audit: boolean, // default true — emit `network.boot.from_env`
}
Read BLAMEJS_* environment variables once and apply the union to the live network facade. Recognised keys cover NTP servers / timeout / drift thresholds, DNS servers / result-order / family / lookup-timeout / cache-TTL / DoH URL or provider / DoT host+port, HTTP_PROXY / HTTPS_PROXY / NO_PROXY, extra-CA file or directory, BLAMEJS_USE_SYSTEM_TRUST, and the socket TCP_NODELAY / SO_KEEPALIVE defaults. Returns an applied report — exactly which keys took effect. Audits network.boot.from_env unless opts.audit:false.
var applied = b.network.bootFromEnv({
env: { BLAMEJS_NTP_SERVERS: "time.cloudflare.com,time.google.com" },
audit: false,
});
applied.ntp.servers;
// → 2
b.network.snapshot() #
Return a redacted snapshot of the network facade's current configuration: NTP servers + drift thresholds, DNS state (servers, result-order, family, DoH/DoT, cache TTL), proxy resolution, TLS trust-store size + system-trust flag, heartbeat statuses, and socket defaults. Cheap; safe to call from a /healthz or operations endpoint. No secrets are returned.
var snap = b.network.snapshot();
typeof snap.tls.caCount;
// → "number"
Last updated 2026-08-08T16:39:49.652Z by seeder.