Real local Unbound cache experiment, not a simulated recursive resolver. PATH check found unbound, unbound-checkconf and /usr/bin/dig; nsd/named were not found on PATH. Unbound1.24.0 (Homebrew), macOS27.0 arm64. Full -V output, configuration, configuration-check result, dig stdout and authority query/ response wire hex are retained in run.jsonl. Existing system DNS was not edited. No dependencies installed. This run did not use a VM or privileged port. Final ports: resolver127.0.0.1:64686, authorityUDP/TCP127.0.0.1:65500. Ports are allocated anew on rerun; there is a small release-to-bind race for the resolver port, which fails visibly if another process takes it. Unbound runs foreground with its own temporary config/log and no pidfile; only that Popen process is terminated. Authority sockets use context managers, thread has60s deadline and2s accepted-TCP timeout; normal stop signal and join are recorded. Final Unbound exit0, authority_thread_stopped=true, and temporary configuration directory removed=true. No other resolver process was touched. Authority is a deliberately bounded teaching wire subset using standard library sockets. UDP and length-prefixed TCP actually worked: direct dig+tcp returned the A answer with AA; Unbound reached authority by UDP. One IN question only, input at most4096B, labels<=63, expanded name<=255, at most16 compression pointers with cycle/bounds checks. Five malformed parser inputs were rejected in a local boundary check (short message, pointer loop, invalid label length, missing question tail, oversized input); a normal question was accepted. Not a hardened public authoritative server or general DNS parser. Supported local A, zoneNS/SOA, NXDOMAIN and NODATA; no DNSSEC, AXFR, IXFR, wildcard, CNAME chasing, arbitrary record types or EDNS response features. SOA serial stays1 during these in-memory A mutations; no secondary propagation or serial-update behavior is claimed. All lab.test data are dedicated examples. Config binds incoming and outgoing interface127.0.0.1, IPv6 disabled, module-config iterator (no validator), do-not-query-localhost:no, prefetch:no, serve-expired:no, cache-min-ttl0, cache-max-negative-ttl3. Default test static zone is explicitly removed with test.nodefault and the more-specific lab.test.transparent permits its stub-zone. Root local-zone refuse rejects other names. stub-addr explicitly points to authority127.0.0.1 at its random port; stub-prime:no and stub-first:no. No root hints, root trust anchor or external server address is supplied. This is a configured direct stub path, not a measured root -> TLD -> authoritative delegation chain. No DNSSEC validation or root priming was exercised. An outside.example query to the resolver actually returned REFUSED with authority-log count10->10; a direct outside query to the teaching authority also returned REFUSED. That is evidence for those tested requests, not a system-wide packet capture proving every possible Unbound background behavior. No public query was requested by this script, and it changes no host resolver configuration. All A records haveTTL3s. Negative responses carry lab.test SOA with RR TTL3 and MINIMUM3, both explicitly in wire/log/dig. Resolver negative TTL cap3 is also explicit. Each expiry test waits until the respective cold response's monotonic timestamp+4.1s, then checks that a new upstream query occurred and the changed answer is visible. This brackets expiry, not a measurement of an exact millisecond of eviction. Fast immediate hits may still displayTTL3 due to second granularity. Latency alone is never used to infer cache hits. Final actual sequence, cumulative authority query counts: Direct authority TCP www A192.0.2.1: count1. Cold resolver www A192.0.2.1:1->2. Cached resolver www A192.0.2.1:2->2. Change authority www to192.0.2.2; direct UDP+norecurse verifies the new address and AA flag (count2->3). Resolver still returns192.0.2.1:3->3. After positive-TTL wait, resolver returns192.0.2.2:3->4. Cold missing.lab.test A yieldsNXDOMAIN with SOA:4->5. Repeat missing NXDOMAIN:5->5. Create missing A192.0.2.3; direct UDP+norecurse verifies the new address and AA flag (count5->6). Resolver still returnsNXDOMAIN:6->6. After negative-TTL wait, resolver returns192.0.2.3:6->7. Existing www AAAA yieldsNOERROR, ANSWER0, SOA (NODATA), not NXDOMAIN. During this stage authority logs an A question then an AAAA question for www, bringing count to9. An immediate same-name A query then returns192.0.2.2 and NOERROR (one more upstream A, count10), demonstrating NODATA did not deny that name's A record. All actual questions remain in evidence; no unique internal resolver reason is assigned to the extra A queries. Outside resolver REFUSED10->10; direct authority outside REFUSED adds query11. Direct authority A response flags qr aa rd, no ra. rd is copied from the request; it does not mean this authority performs recursion. Resolver's stub answers flags qr rd ra without aa. ra indicates availability, not proof that this particular answer required a new upstream lookup. Its local-zone REFUSED response also had aa; this must not be mistaken for an upstream authority answer. NODATA is NOERROR/no requested RR at an existing name; NXDOMAIN is name nonexistence. This run distinguishes their actual response statuses but does not test all negative-cache sharing rules across types/descendants. Initial default sandbox denied the first127.0.0.1 bind with PermissionError1; no service was started in that attempt. Explicitly authorized loopback-socket execution then completed successfully. A second run added outside-zone refusal; a third final run added direct authority verification after each change and A-after-NODATA. All authorized runs completed successfully. Earlier outputs are session-local /tmp/net21-first-success.jsonl and /tmp/net21-second-success.jsonl; run.jsonl contains only the final complete run. Ephemeral ports/queryIDs/timestamps differ between runs. CLI --help0, --bad2, --self-check0. Built-in self-check covers a normal question, four malformed inputs, NODATA and out-of-zone REFUSED. The earlier standalone parser check additionally covered a reserved/oversized label. Python no-excuse checker zero violations; diff --check passed. No commit or push by this worker. Primary documentation checked with the parent researcher: Installed /opt/homebrew/Cellar/unbound/1.24.0/share/man/man5/unbound.conf.5 https://www.nlnetlabs.nl/documentation/unbound/unbound.conf/