Secure Outbound Transport Design
This document details the zero-runtime-dependency outbound transport architecture designed for MultiModel Dev OS v4.3.
1. Node.js Standard Library Architecture
The transport is built strictly using Node.js standard-library modules:
node:https: Client HTTP request creation over TLS.node:dns/promises: Non-blocking asynchronous DNS resolution.node:net: Low-level TCP socket address checks and family detection (isIP,isIPv4,isIPv6).node:tls: TLS connection options and certificate hostname verification.node:stream: Stream handling and AsyncIterable adapters.
2. Address & Destination Policy
URL Parsing & Canonicalization Rules
All provider endpoints undergo strict canonicalization prior to resolution:
- WHATWG URL Parser: Validated exclusively via
new URL(input). - Absolute URL Required: Must be absolute with explicit
https:scheme. Insecurehttp:is rejected. - User Info Rejection: Embedded credentials (e.g.
https://user:pass@host) are rejected. - Fragment Rejection: URLs containing fragments (
#...) are rejected; never silently stripped. - Query String Rejection: Query parameters (
?...) are rejected unless explicitly allowed by endpoint contract. - Control Character & Backslash Rejection: Any URL containing control characters, spaces, or backslashes (
\) is rejected. - Encoded Traversal Rejection: Rejects encoded slashes (
%2f), backslashes (%5c), and path traversal segments (/../,%2e%2e). - Port Policy: Port must be absent or explicitly
443. Alternate HTTPS ports are rejected in v4.3. - Hostname Formatting: Non-ASCII / IDN host input is rejected in v4.3 (IDN support deferred to a future canonicalization policy). Trailing-dot hostnames (e.g.
api.openai.com.) are rejected rather than silently stripped. DNS labels must be valid ASCII labels. - IP Literals: IPv4 literals must be in canonical 4-octet dotted decimal format (
1.2.3.4). Rejects octal, hex, dword, or leading-zero representations. IPv6 literals must be canonical without zone identifiers (%eth0).
IP Address Classification Source & Policy
Classification relies on static, reviewed address rules derived from the IANA IPv4 and IPv6 Special-Purpose Address Registries (Snapshot: 2026-07-27; SHA-256 IPv4: e4a1c06ecf8e934ed5ae30977a1477a78957da1a5fb602fc855e3f74bf01c8ac; SHA-256 IPv6: 8b0e181a4ef0c71fcb25403c40702f2050c2f6dc198156b6ec1a5fb746c9a73e).
Classification Rule: Permit strictly addresses that are explicitly globally reachable. Fail closed on any unknown, malformed, non-global, reserved, or private range.
- IPv4 Rejection:
0.0.0.0/8(Current network / Unspecified)127.0.0.0/8(Loopback)10.0.0.0/8,172.16.0.0/12,192.168.0.0/16(Private-use RFC 1918)169.254.0.0/16(Link-local)224.0.0.0/4(Multicast)240.0.0.0/4(Reserved / Future use)255.255.255.255/32(Broadcast)100.64.0.0/10(Shared Address Space / CGNAT)192.0.0.0/24(IETF Protocol Assignments)192.0.2.0/24,198.51.100.0/24,203.0.113.0/24(Documentation / TEST-NET)198.18.0.0/15(Benchmarking)
- IPv6 Rejection:
::/128(Unspecified)::1/128(Loopback)fc00::/7(Unique Local Unicast - ULA)fe80::/10(Link-Local Unicast)ff00::/8(Multicast)2001:db8::/32(Documentation)::ffff:0:0/96(IPv4-mapped IPv6 - unmapped IPv4 portion normalized and classified against IPv4 rules)
DNS Resolution Architecture & Resolver Trust Model
- Direct Nameserver Queries: DNS resolution plans to use
node:dns/promises(resolve4()andresolve6()), which send network DNS queries directly to configured DNS servers. Standard OS getaddrinfo /dns.lookup()is intentionally avoided during address validation to bypass OShostsfile manipulation. - Resolver Trust & DNS Poisoning Limitations: Direct DNS queries bypass OS
hostsfile lookups, but still query configured DNS servers. These configured servers may be local stub resolvers, enterprise caching proxies, split-horizon resolvers, or compromised DNS servers. Therefore, direct DNS queries do not eliminate DNS poisoning risks; robust IP classification and socket connection pinning remain mandatory. - Fail-Closed Policy: Resolution queries return all IPv4 and IPv6 records. Every returned IP address is classified. If any returned IP address fails the public classification check, the entire resolution fails closed.
- Sprint F1 & F2 Scope: Sprint F1 implements pure destination, IP-address, and resolved-address policies without any default DNS resolver implementation or network execution. Built-in DNS resolution, socket pinning, and native HTTPS transport remain PLANNED for Sprint F2.
3. DNS Rebinding & TOCTOU Protection
To eliminate Time-of-Check to Time-of-Use (TOCTOU) DNS rebinding vulnerabilities:
- Resolve target hostname to IP addresses via
resolve4()/resolve6(). - Validate all returned addresses against IP classification policy.
- Select an approved IP address deterministically.
- Pin socket connection to the selected IP address using standard
lookupoption inhttps.request:javascriptlookup: (hostname, options, callback) => { callback(null, pinnedIpAddress, isIPv6 ? 6 : 4); } - Pass original
hostnameinservername(SNI) andHostheader for TLS certificate identity verification. - Never follow HTTP redirects automatically. Re-resolve and re-validate addresses for every new connection.
4. TLS & Header Policy
TLS Security Baseline
rejectUnauthorized: true: Enforced strictly; cannot be overridden.minVersion: 'TLSv1.2': Modern TLS baseline.- No Custom CAs: System CA store used exclusively; no arbitrary custom CA injections in initial implementation.
- No Ambient Proxies: Ignores
HTTP_PROXY/HTTPS_PROXYenvironment variables.
Header Construction Boundary
Headers are constructed exclusively inside the final transport boundary right before sending:
Authorization:Bearer <secret>(obtained ephemeral viaResolvedCredential.withSecret()).Content-Type:application/json.Accept:application/json(non-stream) ortext/event-stream(stream).User-Agent:MultiModel-Dev-OS-Gateway/4.3.Accept-Encoding:identity(prevents compression bomb attacks).- Rejects any CR/LF (
\r,\n) characters in header keys or values.
5. Resource Limits & Bounded Phase Lifecycle
The transport defines bounded timeouts across 6 separate phases:
- DNS Timeout: 5,000ms limit for resolution.
- Connect Timeout: 5,000ms TCP connection establishment.
- TLS Handshake Timeout: 5,000ms TLS handshake completion.
- Response Header Timeout: 10,000ms wait for response status & headers.
- Idle Response Timeout: 30,000ms inactivity limit between streaming chunks.
- Total Execution Timeout: Bounded by policy
request_timeout_ms(default 30,000ms).
On timeout or AbortSignal trigger:
- Socket destroyed immediately (
socket.destroy()). - In-flight request aborted.
- Ephemeral credential destroyed.
