Mesh DNS: .internal + custom suffix #266

Open
opened 2026-10-31 16:13:25 +00:00 by kayg · 393 comments
Owner

J7 plan: Mesh DNS (<host>.internal plus an optional custom suffix)

Goal and scope

Implement the first Linux slice: an in-binary resolver serving names for configured mesh peers, starting with <host>.internal. A custom suffix such as mesh.kayg.org remains an additional alias per D316; suffix configuration and macOS integration are planned but are not part of this slice. Preserve D165: internal names use the mesh addresses and the resolver does not claim external names.

Plan

  1. Resolver placement by OS. On Linux, bind the resolver only to the mesh interface address. For systemd-resolved, install a per-link ~internal split-DNS route to that address and leave other links and their DNS routes alone. On hosts without systemd-resolved, resolv.conf cannot express split DNS: keep it unchanged by default, and document an explicit forwarding fallback that retains the existing upstream list and sends only .internal answers from the embedded resolver. Do not silently replace the host's DNS configuration. macOS integration will use /etc/resolver/internal (and a matching file for each custom suffix). Where the OS supports a high-port resolver target, prefer an unprivileged listener; privileged resolver installation remains opt-in and only changes the internal suffix route. Slice 1 tests Linux only.
  2. Records. Return A and AAAA for each named mesh peer using its configured mesh IPv4 and IPv6 addresses. Unknown names below an owned suffix return NXDOMAIN. SRV is reserved for service discovery and requires a separate contract; no SRV records in slice 1.
  3. Upstream forwarding. Names outside owned suffixes go to the configured upstream resolver(s), preserving their order and timeout behavior. Do not embed a public resolver default. The mesh-wide configurable upstream from D197 is a later settings slice; slice 1 uses the host's existing upstream configuration when forwarding is enabled. Queries for an unknown name inside .internal never fall through to upstream.
  4. Coordinator distribution. Extend the existing versioned, chunked mesh.configure snapshot with the stable node name alongside its peer addresses. The coordinator remains the authority; peers install a complete name/address snapshot only with the accepted configuration revision. Name changes use the same snapshot/update path, not independent DNS updates.
  5. Conflicts and renames. Reuse the coordinator's existing unique-name allocation and rename rules; never derive DNS names from an IP or silently overwrite a manually chosen name. Names are normalized and validated as one DNS label before publication. When a coordinator rename moves a lower-ranked automatic name aside, the next accepted snapshot replaces both old and new mappings atomically.
  6. TTL and cache. Start with a 30-second positive TTL and 5-second negative TTL. The resolver does not keep a second peer-record cache: its map is the accepted mesh snapshot, replaced atomically. Client caches naturally age out stale names within the advertised TTL after an update.
  7. Coordinator outage (D166). Keep answering from the last accepted snapshot without expiry while the coordinator is unreachable. The mesh and its last-known rules continue; after reconnect, the next accepted configuration removes revoked peers and applies renames.
  8. Size and library decision. Build candidate DNS libraries on compute01 for stripped x86_64 static size. Measure query latency, warmed idle RSS per resolver at 1,000 peer records, and parser correctness on the pinned perf VM through perf-run (performance benchmarks run only there per docs/PROCESS §1). Report candidate numbers and trade-offs to kayg; no foundation library is selected in this job. If a prototype is needed, isolate the smallest candidate behind a clearly named, default-off mesh-dns-spike feature until the owner decides. Report the D243 static binary size delta and stop if the gate requires it.
  9. Tests. Unit-test A/AAAA, canonical suffix matching, NXDOMAIN, malformed and unsupported questions, name conflicts/renames, config replacement, and TTL fields. Exercise real UDP (and TCP if required by the selected crate) on a disposable Linux network namespace with a mesh-address interface; prove names resolve only on the mesh route and unrelated DNS remains on its upstream. Run the repository fast gate on the assigned x86 VM and capture the static-size rows.

Owner question after measurement

Which DNS library, if any, should become the production foundation? The job will bring static-size, RSS-per-resolver, latency, and correctness evidence; the default-off prototype is not a library selection.

Implementation sequence

  1. Add failing resolver and namespace tests behind the spike boundary.
  2. Measure candidates and record evidence here before the owner question.
  3. Implement the Linux in-binary resolver for configured peers; keep host resolver changes explicit and opt-in.
  4. Run targeted tests and scripts/verify fast on claude@infraternal-dev-01-compute01, report D243 delta, commit, push mesh-dns, and post the exact finish evidence here.
## J7 plan: Mesh DNS (`<host>.internal` plus an optional custom suffix) ### Goal and scope Implement the first Linux slice: an in-binary resolver serving names for configured mesh peers, starting with `<host>.internal`. A custom suffix such as `mesh.kayg.org` remains an additional alias per D316; suffix configuration and macOS integration are planned but are not part of this slice. Preserve D165: internal names use the mesh addresses and the resolver does not claim external names. ### Plan 1. **Resolver placement by OS.** On Linux, bind the resolver only to the mesh interface address. For systemd-resolved, install a per-link `~internal` split-DNS route to that address and leave other links and their DNS routes alone. On hosts without systemd-resolved, resolv.conf cannot express split DNS: keep it unchanged by default, and document an explicit forwarding fallback that retains the existing upstream list and sends only `.internal` answers from the embedded resolver. Do not silently replace the host's DNS configuration. macOS integration will use `/etc/resolver/internal` (and a matching file for each custom suffix). Where the OS supports a high-port resolver target, prefer an unprivileged listener; privileged resolver installation remains opt-in and only changes the internal suffix route. Slice 1 tests Linux only. 2. **Records.** Return A and AAAA for each named mesh peer using its configured mesh IPv4 and IPv6 addresses. Unknown names below an owned suffix return NXDOMAIN. SRV is reserved for service discovery and requires a separate contract; no SRV records in slice 1. 3. **Upstream forwarding.** Names outside owned suffixes go to the configured upstream resolver(s), preserving their order and timeout behavior. Do not embed a public resolver default. The mesh-wide configurable upstream from D197 is a later settings slice; slice 1 uses the host's existing upstream configuration when forwarding is enabled. Queries for an unknown name inside `.internal` never fall through to upstream. 4. **Coordinator distribution.** Extend the existing versioned, chunked `mesh.configure` snapshot with the stable node name alongside its peer addresses. The coordinator remains the authority; peers install a complete name/address snapshot only with the accepted configuration revision. Name changes use the same snapshot/update path, not independent DNS updates. 5. **Conflicts and renames.** Reuse the coordinator's existing unique-name allocation and rename rules; never derive DNS names from an IP or silently overwrite a manually chosen name. Names are normalized and validated as one DNS label before publication. When a coordinator rename moves a lower-ranked automatic name aside, the next accepted snapshot replaces both old and new mappings atomically. 6. **TTL and cache.** Start with a 30-second positive TTL and 5-second negative TTL. The resolver does not keep a second peer-record cache: its map is the accepted mesh snapshot, replaced atomically. Client caches naturally age out stale names within the advertised TTL after an update. 7. **Coordinator outage (D166).** Keep answering from the last accepted snapshot without expiry while the coordinator is unreachable. The mesh and its last-known rules continue; after reconnect, the next accepted configuration removes revoked peers and applies renames. 8. **Size and library decision.** Build candidate DNS libraries on compute01 for stripped x86_64 static size. Measure query latency, warmed idle RSS per resolver at 1,000 peer records, and parser correctness on the pinned perf VM through `perf-run` (performance benchmarks run only there per docs/PROCESS §1). Report candidate numbers and trade-offs to kayg; no foundation library is selected in this job. If a prototype is needed, isolate the smallest candidate behind a clearly named, default-off `mesh-dns-spike` feature until the owner decides. Report the D243 static binary size delta and stop if the gate requires it. 9. **Tests.** Unit-test A/AAAA, canonical suffix matching, NXDOMAIN, malformed and unsupported questions, name conflicts/renames, config replacement, and TTL fields. Exercise real UDP (and TCP if required by the selected crate) on a disposable Linux network namespace with a mesh-address interface; prove names resolve only on the mesh route and unrelated DNS remains on its upstream. Run the repository fast gate on the assigned x86 VM and capture the static-size rows. ### Owner question after measurement Which DNS library, if any, should become the production foundation? The job will bring static-size, RSS-per-resolver, latency, and correctness evidence; the default-off prototype is not a library selection. ### Implementation sequence 1. Add failing resolver and namespace tests behind the spike boundary. 2. Measure candidates and record evidence here before the owner question. 3. Implement the Linux in-binary resolver for configured peers; keep host resolver changes explicit and opt-in. 4. Run targeted tests and `scripts/verify fast` on `claude@infraternal-dev-01-compute01`, report D243 delta, commit, push `mesh-dns`, and post the exact finish evidence here.
Author
Owner

Starting J7 on branch mesh-dns, worktree /home/kayg/Developer/Worktrees/infraternal-mesh-dns, based on origin/dev at 42ab330358eb06d47acade8d0f2c35b9225af74b.

Starting J7 on branch `mesh-dns`, worktree `/home/kayg/Developer/Worktrees/infraternal-mesh-dns`, based on `origin/dev` at `42ab330358eb06d47acade8d0f2c35b9225af74b`.
Author
Owner

Finding: crates/plugins/mesh/src/configuration.rs::compile already walks every coordinator member and retains denied peers; member.node.host supplies the stable coordinator name. The wire Peer currently carries only key, addresses, and endpoint, so names need an additive field on the existing versioned config path. configuration::chunks applies diffs by peer and the node stages them until the last chunk, which gives name/address replacement an existing atomic boundary. store.rs::attach_named ranks hand-picked names above server/inventory/reported names and suffixes weaker collisions (for example minipc01-2); automatic renames are audited. Node::restore replays the last accepted config, consistent with D166 outage behavior.

Finding: `crates/plugins/mesh/src/configuration.rs::compile` already walks every coordinator member and retains denied peers; `member.node.host` supplies the stable coordinator name. The wire `Peer` currently carries only key, addresses, and endpoint, so names need an additive field on the existing versioned config path. `configuration::chunks` applies diffs by peer and the node stages them until the last chunk, which gives name/address replacement an existing atomic boundary. `store.rs::attach_named` ranks hand-picked names above server/inventory/reported names and suffixes weaker collisions (for example `minipc01-2`); automatic renames are audited. `Node::restore` replays the last accepted config, consistent with D166 outage behavior.
Author
Owner

Build-path finding: the assigned host resolves to fd66:6821:bc63:834e:3a2:f7f3:abd5:bf2c; the local route is via wt0, but ssh -o BatchMode=yes -o ConnectTimeout=10 claude@infraternal-dev-01-compute01 returns No route to host. No T3 environment is linked, so I requested a link for this named host and will continue code preparation while that request is pending. Candidate measurements and the required x86 gate are waiting on access to this host.

Build-path finding: the assigned host resolves to `fd66:6821:bc63:834e:3a2:f7f3:abd5:bf2c`; the local route is via `wt0`, but `ssh -o BatchMode=yes -o ConnectTimeout=10 claude@infraternal-dev-01-compute01` returns `No route to host`. No T3 environment is linked, so I requested a link for this named host and will continue code preparation while that request is pending. Candidate measurements and the required x86 gate are waiting on access to this host.
Author
Owner

TDD RED evidence (dev-02, command: cargo test -p infraternal-plugin-mesh --features server --test configuration compiled_snapshot_carries_local_and_peer_names_even_when_access_is_denied -j3 under build-slot): compilation failed only because Config, Peer, and Chunk have no name field (seven E0609 diagnostics). This confirms the existing snapshot cannot yet distribute local/peer names.

TDD RED evidence (dev-02, command: `cargo test -p infraternal-plugin-mesh --features server --test configuration compiled_snapshot_carries_local_and_peer_names_even_when_access_is_denied -j3` under `build-slot`): compilation failed only because `Config`, `Peer`, and `Chunk` have no `name` field (seven E0609 diagnostics). This confirms the existing snapshot cannot yet distribute local/peer names.
Author
Owner

Finding (dev-02, build-slot): cargo test -p infraternal-plugin-mesh --features server --test configuration -j3 completed with test result: ok. 7 passed; 0 failed; 0 ignored. This covers local and denied-peer names in snapshots and atomic peer rename chunks. Old strict receivers reject unknown chunk fields, so name fields will be guarded by explicit dns_names capability negotiation.

Finding (dev-02, `build-slot`): `cargo test -p infraternal-plugin-mesh --features server --test configuration -j3` completed with `test result: ok. 7 passed; 0 failed; 0 ignored`. This covers local and denied-peer names in snapshots and atomic peer rename chunks. Old strict receivers reject unknown chunk fields, so name fields will be guarded by explicit `dns_names` capability negotiation.
Author
Owner

Build host is back. ssh claude@infraternal-dev-01-compute01 true succeeds; the x86 baseline static build is queued through ~/infraternal-ops/build-slot using scripts/auth-toolchain and scripts/static-cargo. The earlier direct cargo build attempt was not a valid baseline: OpenSSL failed because the static Zig tool was not staged in that shell.

Build host is back. `ssh claude@infraternal-dev-01-compute01 true` succeeds; the x86 baseline static build is queued through `~/infraternal-ops/build-slot` using `scripts/auth-toolchain` and `scripts/static-cargo`. The earlier direct `cargo build` attempt was not a valid baseline: OpenSSL failed because the static Zig tool was not staged in that shell.
Author
Owner

TDD RED (dev-02, build-slot, cargo test -p infraternal-plugin-mesh --features server,agent --test access_updates compact_identity_support_requires_an_explicit_strict_probe -j3): test compact_identity_support_requires_an_explicit_strict_probe ... FAILED; the assertion for {"compact_access":true,"dns_names":true} failed because the strict identity parser rejected the new capability. This confirms the coordinator must explicitly negotiate support before sending name fields.

TDD RED (dev-02, `build-slot`, `cargo test -p infraternal-plugin-mesh --features server,agent --test access_updates compact_identity_support_requires_an_explicit_strict_probe -j3`): `test compact_identity_support_requires_an_explicit_strict_probe ... FAILED`; the assertion for `{"compact_access":true,"dns_names":true}` failed because the strict identity parser rejected the new capability. This confirms the coordinator must explicitly negotiate support before sending name fields.
Author
Owner

TDD RED (dev-02, build-slot, cargo test -p infraternal-plugin-mesh --features server,agent --lib identity_upgrade_tests -j3): the new reply-shape test failed to compile because the old Identity has no dns_names field and the old response helper takes only the compact-access probe. The next change adds the separately negotiated flag while preserving the legacy two-field reply.

TDD RED (dev-02, `build-slot`, `cargo test -p infraternal-plugin-mesh --features server,agent --lib identity_upgrade_tests -j3`): the new reply-shape test failed to compile because the old `Identity` has no `dns_names` field and the old response helper takes only the compact-access probe. The next change adds the separately negotiated flag while preserving the legacy two-field reply.
Author
Owner

Plan implementation detail: the Linux listener uses unprivileged UDP port 1053, bound only to the mesh IPv4 address. This follows D316's rootless-by-default constraint where the OS resolver supports a per-route port (systemd-resolved DNS address with #1053; macOS resolver files use port 1053). Slice 1 does not edit host resolver configuration. With split routing, only .internal reaches this listener and other suffixes keep using their existing OS routes; the in-binary resolver does not forward external queries. The /etc/resolv.conf fallback remains an explicit later integration because its nameserver syntax cannot express a port or split domain without a local forwarding layer.

Plan implementation detail: the Linux listener uses unprivileged UDP port 1053, bound only to the mesh IPv4 address. This follows D316's rootless-by-default constraint where the OS resolver supports a per-route port (`systemd-resolved` DNS address with `#1053`; macOS resolver files use `port 1053`). Slice 1 does not edit host resolver configuration. With split routing, only `.internal` reaches this listener and other suffixes keep using their existing OS routes; the in-binary resolver does not forward external queries. The `/etc/resolv.conf` fallback remains an explicit later integration because its nameserver syntax cannot express a port or split domain without a local forwarding layer.
Author
Owner

Test-plan scope correction for slice 1: the disposable network-namespace test verifies that UDP DNS answers arrive only on the mesh address, that an accepted rename retires the old mapping and publishes the new one, and that a direct query for an external name receives no mesh answer. Slice 1 never edits system resolver routes, so external OS lookups retain their existing upstream routing; proving that end-to-end requires the later Linux/macOS split-DNS installer. The resolver's user-space listener is on UDP/1053 (the namespace test still needs root only to create/configure the isolated network namespace).

Test-plan scope correction for slice 1: the disposable network-namespace test verifies that UDP DNS answers arrive only on the mesh address, that an accepted rename retires the old mapping and publishes the new one, and that a direct query for an external name receives no mesh answer. Slice 1 never edits system resolver routes, so external OS lookups retain their existing upstream routing; proving that end-to-end requires the later Linux/macOS split-DNS installer. The resolver's user-space listener is on UDP/1053 (the namespace test still needs root only to create/configure the isolated network namespace).
Author
Owner

TDD RED evidence (dev-02, build-slot, cargo test -p infraternal-plugin-mesh --features mesh-dns-spike --test dns -j3): the initial resolver test compile failed because infraternal_plugin_mesh::dns and the simple-dns dependency were absent. After implementing the resolver, the next compile exposed a borrow conflict in endpoint-only configuration publication and test assumptions about the library's A/AAAA fields and borrowed packet buffer lifetime; those are fixed, and the targeted test is running again.

TDD RED evidence (dev-02, `build-slot`, `cargo test -p infraternal-plugin-mesh --features mesh-dns-spike --test dns -j3`): the initial resolver test compile failed because `infraternal_plugin_mesh::dns` and the `simple-dns` dependency were absent. After implementing the resolver, the next compile exposed a borrow conflict in endpoint-only configuration publication and test assumptions about the library's A/AAAA fields and borrowed packet buffer lifetime; those are fixed, and the targeted test is running again.
Author
Owner

Finding: the versioned snapshot path carries the local name and peer names (including policy-denied peers) and applies peer renames with the existing final-chunk boundary. On dev-02 through build-slot, the configuration target passed 7/7, the strict identity capability probe passed 1/1, the legacy identity reply-shape test passed 1/1, and the DNS target passed 6/6 (one root-only namespace case remains queued). DNS unit coverage includes local/peer A and AAAA, case folding, positive and negative TTLs, rename replacement, duplicate/invalid names, suffix boundaries, unsupported SRV, malformed queries, and external-name pass-through.

Finding: the versioned snapshot path carries the local name and peer names (including policy-denied peers) and applies peer renames with the existing final-chunk boundary. On dev-02 through `build-slot`, the configuration target passed 7/7, the strict identity capability probe passed 1/1, the legacy identity reply-shape test passed 1/1, and the DNS target passed 6/6 (one root-only namespace case remains queued). DNS unit coverage includes local/peer A and AAAA, case folding, positive and negative TTLs, rename replacement, duplicate/invalid names, suffix boundaries, unsupported SRV, malformed queries, and external-name pass-through.
Author
Owner

D243 baseline evidence: the unmodified origin/dev worktree at 42ab330358eb06d47acade8d0f2c35b9225af74b built on compute01 with scripts/static-cargo through build-slot; the stripped x86_64 binary is 21,431,472 bytes, matching the current D243 ceiling. I will report the branch-head delta from the final x86 gate.

D243 baseline evidence: the unmodified `origin/dev` worktree at `42ab330358eb06d47acade8d0f2c35b9225af74b` built on compute01 with `scripts/static-cargo` through `build-slot`; the stripped x86_64 binary is 21,431,472 bytes, matching the current D243 ceiling. I will report the branch-head delta from the final x86 gate.
Author
Owner

Finding: with mesh-dns-spike, configuration::compile copies every member.node.host into peer DNS records, and node coordination distributes them to every DNS-capable peer, including denied peers. Existing api::decode_host deliberately maps reported/placeholder names to unnamed-<id> for viewers without host facts; review_mesh_view_truth_r3::a_non_owner_cannot_confirm_the_servers_hidden_hostname checks that privacy boundary. A DNS query from a mesh host could reveal the hidden reported hostname. Evidence: crates/plugins/mesh/src/configuration.rs:28,79, crates/plugins/mesh/src/node_coordination.rs:216-220, crates/plugins/mesh/src/api.rs:1945-1955, and crates/plugins/mesh/tests/review_mesh_view_truth_r3.rs:155. The spike remains default-off. Owner question: should DNS use the same privacy-safe unnamed-<id> projection for automatic reported names, or are raw reported names intentionally visible to every mesh host? I am continuing validation without blocking on an answer.

Finding: with `mesh-dns-spike`, `configuration::compile` copies every `member.node.host` into peer DNS records, and node coordination distributes them to every DNS-capable peer, including denied peers. Existing `api::decode_host` deliberately maps `reported`/placeholder names to `unnamed-<id>` for viewers without host facts; `review_mesh_view_truth_r3::a_non_owner_cannot_confirm_the_servers_hidden_hostname` checks that privacy boundary. A DNS query from a mesh host could reveal the hidden reported hostname. Evidence: `crates/plugins/mesh/src/configuration.rs:28,79`, `crates/plugins/mesh/src/node_coordination.rs:216-220`, `crates/plugins/mesh/src/api.rs:1945-1955`, and `crates/plugins/mesh/tests/review_mesh_view_truth_r3.rs:155`. The spike remains default-off. Owner question: should DNS use the same privacy-safe `unnamed-<id>` projection for automatic reported names, or are raw reported names intentionally visible to every mesh host? I am continuing validation without blocking on an answer.
Author
Owner

Finding: The full feature-enabled mesh crate suite on dev-02 passed (exit 0), and the root namespace test passed: test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok; test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 7 filtered out. The first compile-out suite attempt failed while writing incremental test metadata with error: couldn't create a temp dir: No space left on device (os error 28) under the job target. Immediately afterward df -h /home/claude showed 24 GiB free; the cause may be transient or quota-related. The namespace cleanup check found no j7-mesh-dns namespace. I removed only this job's 5.2 GiB generated target tree and restarted the compile-out run with debug info and incremental compilation disabled through build-slot; that retry is queued.

Finding: The full feature-enabled mesh crate suite on dev-02 passed (exit 0), and the root namespace test passed: `test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok`; `test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 7 filtered out`. The first compile-out suite attempt failed while writing incremental test metadata with `error: couldn't create a temp dir: No space left on device (os error 28)` under the job target. Immediately afterward `df -h /home/claude` showed 24 GiB free; the cause may be transient or quota-related. The namespace cleanup check found no `j7-mesh-dns` namespace. I removed only this job's 5.2 GiB generated target tree and restarted the compile-out run with debug info and incremental compilation disabled through `build-slot`; that retry is queued.
Author
Owner

Finding: feature-enabled clippy initially failed at crates/plugins/mesh/src/node_actions.rs:336 with error: used underscore-prefixed binding; the identity capability argument was needed only when mesh-dns-spike is enabled. I renamed the parameter and explicitly consume it in compile-out builds. The updated source is on the dev-02 job worktree; clippy and the compile-out suite are queued for rerun through build-slot.

Finding: feature-enabled clippy initially failed at `crates/plugins/mesh/src/node_actions.rs:336` with `error: used underscore-prefixed binding`; the identity capability argument was needed only when `mesh-dns-spike` is enabled. I renamed the parameter and explicitly consume it in compile-out builds. The updated source is on the dev-02 job worktree; clippy and the compile-out suite are queued for rerun through `build-slot`.
Author
Owner

Measurement lane correction: docs/PROCESS §1 restricts performance benchmarks to the pinned perf VM. I updated plan item 8: compute01 builds and measures stripped x86_64 size; query latency, warmed RSS and parser correctness run through perf-run on perf-01.

Measurement lane correction: docs/PROCESS §1 restricts performance benchmarks to the pinned perf VM. I updated plan item 8: compute01 builds and measures stripped x86_64 size; query latency, warmed RSS and parser correctness run through `perf-run` on perf-01.
Author
Owner

Blocked status (2026-10-31 18:15 UTC): the compile-out mesh suite has been queued in dev-02's build-slot since 18:04 UTC; all 3/3 slots are occupied and this job has no compiler process running. The compute01 x86 candidate build remains queued since before 17:35 UTC with all 4/4 slots occupied. Its build-slot process is still active, and the candidate harness now builds static images only; latency/RSS/correctness are reserved for perf-01 via perf-run. The dev-02 target is clean with 79 GiB free. I am leaving both queues intact and continuing work that does not need those slots.

Blocked status (2026-10-31 18:15 UTC): the compile-out mesh suite has been queued in dev-02's `build-slot` since 18:04 UTC; all 3/3 slots are occupied and this job has no compiler process running. The compute01 x86 candidate build remains queued since before 17:35 UTC with all 4/4 slots occupied. Its build-slot process is still active, and the candidate harness now builds static images only; latency/RSS/correctness are reserved for perf-01 via `perf-run`. The dev-02 target is clean with 79 GiB free. I am leaving both queues intact and continuing work that does not need those slots.
Author
Owner

Mesh DNS implementation plan (J7)

  • Resolver placement: Slice 1 adds a Linux UDP listener bound only to the mesh IPv4 address on unprivileged port 1053; it does not edit host resolver settings. The Linux integration can route .internal (and a future configured suffix) to mesh-ip#1053 through systemd-resolved. A plain /etc/resolv.conf nameserver entry cannot express either a port or split domain, so that fallback needs a local forwarding stub. On macOS, the split route is /etc/resolver/<suffix> with the mesh address and port 1053; use a per-user route where the OS supports one and require system-level changes only where needed.
  • Records: A and AAAA map each local/peer name to its mesh addresses. SRV is deferred until a mesh service contract needs it; unsupported types return an empty authoritative answer.
  • Forwarding: The mesh listener handles only its configured suffix and never captures external names. The OS keeps routing other suffixes to its current upstream. If mesh-wide upstream forwarding is added later (D197), it must be explicit and avoid forwarding loops.
  • Name distribution: The coordinator remains authoritative. DNS-capable nodes negotiate the dns_names extension and receive local/peer names on the existing versioned, chunked snapshot path; a complete accepted revision replaces the answer set atomically.
  • Conflicts and renames: Reuse the coordinator's existing unique-name ranking and audited rename behavior. Reject malformed or duplicate labels instead of selecting an arbitrary address. An accepted rename removes the old label and installs the new one in the same snapshot commit. The privacy question below remains open for automatically reported hostnames.
  • TTL/cache: Start with 30-second positive and 5-second negative TTLs. The resolver holds only the authoritative snapshot; the OS cache follows those TTLs.
  • Coordinator outage: Keep the last accepted DNS snapshot indefinitely and restore it with the mesh config, matching D166's no-expiry behavior. A removal or rename takes effect when its accepted update reaches the node.
  • Size and library choice: The current x86 D243 ceiling is 21,431,472 bytes with no slack. mesh-dns-spike is default-off, so the shipping build is unchanged. Build baseline, simple-dns, and hickory-proto candidates on compute01; measure query correctness, latency, and warmed RSS at 1,000 records on perf-01. Report size/RSS/latency/correctness trade-offs to kayg; production library choice stays with the owner.
  • Tests/gates: Unit tests cover A/AAAA, suffix boundaries, malformed and EDNS queries, unsupported types, duplicate names, and positive/negative TTLs. Snapshot tests cover denied peers and atomic rename; a root-only disposable network-namespace test proves binding and UDP replies only on the mesh address. Run feature-on and feature-off mesh suites, clippy, fuzz compile-check, x86 static-size and scripts/verify fast. OS resolver installation/forwarding tests belong to the later installer slice.
## Mesh DNS implementation plan (J7) - **Resolver placement:** Slice 1 adds a Linux UDP listener bound only to the mesh IPv4 address on unprivileged port 1053; it does not edit host resolver settings. The Linux integration can route `.internal` (and a future configured suffix) to `mesh-ip#1053` through `systemd-resolved`. A plain `/etc/resolv.conf` nameserver entry cannot express either a port or split domain, so that fallback needs a local forwarding stub. On macOS, the split route is `/etc/resolver/<suffix>` with the mesh address and `port 1053`; use a per-user route where the OS supports one and require system-level changes only where needed. - **Records:** A and AAAA map each local/peer name to its mesh addresses. SRV is deferred until a mesh service contract needs it; unsupported types return an empty authoritative answer. - **Forwarding:** The mesh listener handles only its configured suffix and never captures external names. The OS keeps routing other suffixes to its current upstream. If mesh-wide upstream forwarding is added later (D197), it must be explicit and avoid forwarding loops. - **Name distribution:** The coordinator remains authoritative. DNS-capable nodes negotiate the `dns_names` extension and receive local/peer names on the existing versioned, chunked snapshot path; a complete accepted revision replaces the answer set atomically. - **Conflicts and renames:** Reuse the coordinator's existing unique-name ranking and audited rename behavior. Reject malformed or duplicate labels instead of selecting an arbitrary address. An accepted rename removes the old label and installs the new one in the same snapshot commit. The privacy question below remains open for automatically reported hostnames. - **TTL/cache:** Start with 30-second positive and 5-second negative TTLs. The resolver holds only the authoritative snapshot; the OS cache follows those TTLs. - **Coordinator outage:** Keep the last accepted DNS snapshot indefinitely and restore it with the mesh config, matching D166's no-expiry behavior. A removal or rename takes effect when its accepted update reaches the node. - **Size and library choice:** The current x86 D243 ceiling is 21,431,472 bytes with no slack. `mesh-dns-spike` is default-off, so the shipping build is unchanged. Build baseline, `simple-dns`, and `hickory-proto` candidates on compute01; measure query correctness, latency, and warmed RSS at 1,000 records on perf-01. Report size/RSS/latency/correctness trade-offs to kayg; production library choice stays with the owner. - **Tests/gates:** Unit tests cover A/AAAA, suffix boundaries, malformed and EDNS queries, unsupported types, duplicate names, and positive/negative TTLs. Snapshot tests cover denied peers and atomic rename; a root-only disposable network-namespace test proves binding and UDP replies only on the mesh address. Run feature-on and feature-off mesh suites, clippy, fuzz compile-check, x86 static-size and `scripts/verify fast`. OS resolver installation/forwarding tests belong to the later installer slice.
Author
Owner

Status update: compute01 is reachable after the owner reboot, but all four build slots are still occupied. The x86 candidate static comparison remains queued and has not produced artifacts yet. Dev-02's compile-out mesh suite is also queued; the feature-on mesh suite and root namespace test passed as recorded above. I am continuing source review and queued validations; host connectivity is no longer the blocker.

Status update: compute01 is reachable after the owner reboot, but all four build slots are still occupied. The x86 candidate static comparison remains queued and has not produced artifacts yet. Dev-02's compile-out mesh suite is also queued; the feature-on mesh suite and root namespace test passed as recorded above. I am continuing source review and queued validations; host connectivity is no longer the blocker.
Author
Owner

Finding: the compile-out mesh crate suite on dev-02 has now passed through build-slot (exit 0), confirming the default-off feature keeps the existing mesh build intact. I added a focused test for SRV NODATA to require the planned 5-second SOA negative TTL; it is queued against the current resolver implementation to capture the expected red result before the code change. The x86 candidate static comparison remains queued on compute01.

Finding: the compile-out mesh crate suite on dev-02 has now passed through `build-slot` (exit 0), confirming the default-off feature keeps the existing mesh build intact. I added a focused test for SRV NODATA to require the planned 5-second SOA negative TTL; it is queued against the current resolver implementation to capture the expected red result before the code change. The x86 candidate static comparison remains queued on compute01.
Author
Owner

Finding (dev-02, through build-slot): the focused unsupported_record_types_return_a_five_second_negative_ttl test failed as expected. For an existing build-01.internal queried as SRV, the response was NOERROR with zero answers and zero authority records (assertion left: 0, right: 1 at tests/dns.rs:160). The unsupported-type branch returned NODATA without the SOA, so clients could not apply the planned 5-second negative cache TTL. I am adding the existing zone SOA to NODATA responses while keeping the answer set empty.

Finding (dev-02, through `build-slot`): the focused `unsupported_record_types_return_a_five_second_negative_ttl` test failed as expected. For an existing `build-01.internal` queried as SRV, the response was NOERROR with zero answers and zero authority records (`assertion left: 0, right: 1` at `tests/dns.rs:160`). The unsupported-type branch returned NODATA without the SOA, so clients could not apply the planned 5-second negative cache TTL. I am adding the existing zone SOA to NODATA responses while keeping the answer set empty.
Author
Owner

Finding resolved: after adding the zone SOA to NODATA responses, the dev-02 focused regression passed: test unsupported_record_types_return_a_five_second_negative_ttl ... ok; test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 8 filtered out. The full feature-enabled mesh suite is queued next. The compute01 candidate static comparison is still waiting for an x86 build slot.

Finding resolved: after adding the zone SOA to NODATA responses, the dev-02 focused regression passed: `test unsupported_record_types_return_a_five_second_negative_ttl ... ok`; `test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 8 filtered out`. The full feature-enabled mesh suite is queued next. The compute01 candidate static comparison is still waiting for an x86 build slot.
Author
Owner

Finding: the root-only network namespace test passed on dev-02 through ssh cua + sudo and build-slot: test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok; test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 8 filtered out. The test verified mesh-address UDP answering, atomic rename visibility, and silence for external names. The EXIT cleanup removed the disposable namespace; ip netns list and the job namespace path check returned no leftovers.

Finding: the root-only network namespace test passed on dev-02 through `ssh cua` + sudo and `build-slot`: `test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok`; `test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 8 filtered out`. The test verified mesh-address UDP answering, atomic rename visibility, and silence for external names. The EXIT cleanup removed the disposable namespace; `ip netns list` and the job namespace path check returned no leftovers.
Author
Owner

Queue update: compute01 is reachable but still has no free build slot. The candidate size comparison has been queued through build-slot since before the reboot and still has no artifacts; dev-02's feature-on/off and namespace tests have completed, but the final feature-on clippy check is queued behind other jobs. I am preserving both queued commands and continuing the non-build review/preparation.

Queue update: compute01 is reachable but still has no free build slot. The candidate size comparison has been queued through `build-slot` since before the reboot and still has no artifacts; dev-02's feature-on/off and namespace tests have completed, but the final feature-on clippy check is queued behind other jobs. I am preserving both queued commands and continuing the non-build review/preparation.
Author
Owner

Candidate measurement from compute01 and perf-01 (isolated harnesses, not final binary): baseline / simple-dns / hickory-proto stripped static sizes were 414,328 / 519,992 / 759,192 bytes, deltas +105,664 / +344,864 bytes. At 1,000 peer records, warmed idle RSS was 412 / 552 / 664 KiB. Mean parser+encoder time over 200,000 queries was 4,749 / 5,104 ns; the limited correctness corpus passed 8/8 queries and rejected 3/3 malformed packets for both candidates. This is parser+encoder time, not end-to-end DNS latency, and the harness does not make either candidate the production choice. simple-dns is the smaller candidate and remains only behind the default-off spike. Owner decision remains open: select a library or request another measurement.

Candidate measurement from compute01 and perf-01 (isolated harnesses, not final binary): baseline / simple-dns / hickory-proto stripped static sizes were 414,328 / 519,992 / 759,192 bytes, deltas +105,664 / +344,864 bytes. At 1,000 peer records, warmed idle RSS was 412 / 552 / 664 KiB. Mean parser+encoder time over 200,000 queries was 4,749 / 5,104 ns; the limited correctness corpus passed 8/8 queries and rejected 3/3 malformed packets for both candidates. This is parser+encoder time, not end-to-end DNS latency, and the harness does not make either candidate the production choice. simple-dns is the smaller candidate and remains only behind the default-off spike. Owner decision remains open: select a library or request another measurement.
Author
Owner

Finding: EDNS0 queries received the mesh A answer but lost the OPT record because simple_dns::Packet::into_reply() replaces the header. The new regression first failed at the OPT-presence assertion; cloning the parsed OPT before conversion and restoring it to the reply fixed the path. Focused dev-02 retest through build-slot: test edns0_opt_queries_still_receive_mesh_answers ... ok; test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 8 filtered out. The full feature-on suite, feature-on clippy, and refreshed root network-namespace run are queued/running.

Finding: EDNS0 queries received the mesh A answer but lost the OPT record because `simple_dns::Packet::into_reply()` replaces the header. The new regression first failed at the OPT-presence assertion; cloning the parsed OPT before conversion and restoring it to the reply fixed the path. Focused dev-02 retest through `build-slot`: `test edns0_opt_queries_still_receive_mesh_answers ... ok`; `test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 8 filtered out`. The full feature-on suite, feature-on clippy, and refreshed root network-namespace run are queued/running.
Author
Owner

Finding: the feature-off mesh suite passed on dev-02, and the focused DNS target passed with test result: ok. 8 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out. The first fuzz compile reached its separate workspace but cargo check --locked exited 101 because crates/plugins/mesh/fuzz/Cargo.lock needs updates for the new DNS dependencies. I am regenerating that tracked lock and will verify the fuzz target again with --locked.

Finding: the feature-off mesh suite passed on dev-02, and the focused DNS target passed with `test result: ok. 8 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out`. The first fuzz compile reached its separate workspace but `cargo check --locked` exited 101 because `crates/plugins/mesh/fuzz/Cargo.lock` needs updates for the new DNS dependencies. I am regenerating that tracked lock and will verify the fuzz target again with `--locked`.
Author
Owner

Finding: the refreshed root-only network-namespace regression reproduced an oversized UDP truncation bug. It sent a 4,097-byte valid-prefix query; the 4,096-byte recv_from buffer silently truncated it and the listener replied as if the prefix were a complete query. Evidence: dev-02 root run under ssh cua + sudo + build-slot failed at tests/dns.rs:321 with a truncated oversized datagram must not be parsed as its valid prefix. The script's EXIT trap ran; I am checking that no namespace remains, then will make the receive buffer one byte larger than the parser limit and drop over-limit datagrams.

Finding: the refreshed root-only network-namespace regression reproduced an oversized UDP truncation bug. It sent a 4,097-byte valid-prefix query; the 4,096-byte `recv_from` buffer silently truncated it and the listener replied as if the prefix were a complete query. Evidence: dev-02 root run under `ssh cua` + sudo + `build-slot` failed at `tests/dns.rs:321` with `a truncated oversized datagram must not be parsed as its valid prefix`. The script's EXIT trap ran; I am checking that no namespace remains, then will make the receive buffer one byte larger than the parser limit and drop over-limit datagrams.
Author
Owner

Status: compute01 and dev-02 are reachable. The final scripts/verify fast command has been queued on compute01 through build-slot and still reports all 4 slots busy; the locked fuzz check, updated DNS test target, and feature-on clippy are queued on dev-02. Dev-02 currently has 33 GiB free. These are slot waits, not command failures; all queues remain active while I continue the slice.

Status: compute01 and dev-02 are reachable. The final `scripts/verify fast` command has been queued on compute01 through `build-slot` and still reports all 4 slots busy; the locked fuzz check, updated DNS test target, and feature-on clippy are queued on dev-02. Dev-02 currently has 33 GiB free. These are slot waits, not command failures; all queues remain active while I continue the slice.
Author
Owner

Finding resolved: after regenerating crates/plugins/mesh/fuzz/Cargo.lock, the locked fuzz target check passed on dev-02 through build-slot: cargo check --locked --manifest-path crates/plugins/mesh/fuzz/Cargo.toml --bin dns -j3 completed with Finished dev profile ... in 0.27s. The lock update is committed as e20cbb4fb and pushed with the branch rebased onto current origin/dev 57491e918. The oversize UDP regression is now green in the ordinary DNS target; the root namespace confirmation remains queued.

Finding resolved: after regenerating `crates/plugins/mesh/fuzz/Cargo.lock`, the locked fuzz target check passed on dev-02 through `build-slot`: `cargo check --locked --manifest-path crates/plugins/mesh/fuzz/Cargo.toml --bin dns -j3` completed with `Finished dev profile ... in 0.27s`. The lock update is committed as `e20cbb4fb` and pushed with the branch rebased onto current `origin/dev` `57491e918`. The oversize UDP regression is now green in the ordinary DNS target; the root namespace confirmation remains queued.
Author
Owner

Correction to my previous comment: the ordinary DNS target passed 8 tests with the root-only namespace test ignored. That run does not execute the oversized-datagram assertion. The namespace confirmation is still queued, so the oversized-path regression remains pending until that root test passes.

Correction to my previous comment: the ordinary DNS target passed 8 tests with the root-only namespace test ignored. That run does not execute the oversized-datagram assertion. The namespace confirmation is still queued, so the oversized-path regression remains pending until that root test passes.
Author
Owner

Owner questions from #266 candidate measurements:

  1. Which DNS parser library, if any, should become the foundation for the full resolver: simple-dns, hickory-proto, or another measured candidate? In stripped isolated harnesses the sizes were 414,328 B baseline, 519,992 B simple-dns (+105,664 B), and 759,192 B hickory-proto (+344,864 B). At 1,000 peer records, warmed RSS was 412 / 552 / 664 KiB; parser+encoder means were 4,749 / 5,104 ns for simple-dns / hickory-proto. Both passed the limited 8/8 correctness and 3/3 malformed corpus. These parser timings are not end-to-end DNS latency. The branch uses simple-dns only behind the default-off mesh-dns-spike flag; it does not select the foundation library.

  2. Should DNS publish coordinator host labels for every mesh member, including peers whose traffic policy is denied, or should it expose only names allowed by a separate rule? The prototype currently carries all coordinator member names in the accepted snapshot, so this is a hostname-visibility choice for the owner.

Owner questions from #266 candidate measurements: 1. Which DNS parser library, if any, should become the foundation for the full resolver: simple-dns, hickory-proto, or another measured candidate? In stripped isolated harnesses the sizes were 414,328 B baseline, 519,992 B simple-dns (+105,664 B), and 759,192 B hickory-proto (+344,864 B). At 1,000 peer records, warmed RSS was 412 / 552 / 664 KiB; parser+encoder means were 4,749 / 5,104 ns for simple-dns / hickory-proto. Both passed the limited 8/8 correctness and 3/3 malformed corpus. These parser timings are not end-to-end DNS latency. The branch uses simple-dns only behind the default-off `mesh-dns-spike` flag; it does not select the foundation library. 2. Should DNS publish coordinator host labels for every mesh member, including peers whose traffic policy is denied, or should it expose only names allowed by a separate rule? The prototype currently carries all coordinator member names in the accepted snapshot, so this is a hostname-visibility choice for the owner.
Author
Owner

Finding resolved on final head ae1004a53fd938e8f3e0efde7d6712846be179f7: on dev-02 through build-slot, cargo test --locked -p infraternal-plugin-mesh --features server,agent,mesh-dns-spike --all-targets -j3 exited 0. The DNS target reported 8 passed; 0 failed; 1 ignored; the ignored test is the root-only namespace integration test. That test is queued separately through ssh cua + sudo + build-slot. The final x86 scripts/verify fast run is queued on compute01 at the same head.

Finding resolved on final head `ae1004a53fd938e8f3e0efde7d6712846be179f7`: on dev-02 through `build-slot`, `cargo test --locked -p infraternal-plugin-mesh --features server,agent,mesh-dns-spike --all-targets -j3` exited 0. The DNS target reported `8 passed; 0 failed; 1 ignored`; the ignored test is the root-only namespace integration test. That test is queued separately through `ssh cua` + sudo + `build-slot`. The final x86 `scripts/verify fast` run is queued on compute01 at the same head.
Author
Owner

Finding: the first root namespace runner exited 0 without executing the ignored integration test. It selected the newest dns-* executable by timestamp; dns-67fdf36cfa37d7ac --list reports 0 tests, while dns-75ff6ac5a04b8625 --list contains the expected namespace test among 9 tests. The runner's selection was wrong, not a resolver test result. Its EXIT trap ran and sudo ip netns list showed no leftover namespace. I am updating the temporary runner to select a binary by the test name and require exactly one passing test before reporting the namespace gate.

Finding: the first root namespace runner exited 0 without executing the ignored integration test. It selected the newest `dns-*` executable by timestamp; `dns-67fdf36cfa37d7ac --list` reports 0 tests, while `dns-75ff6ac5a04b8625 --list` contains the expected namespace test among 9 tests. The runner's selection was wrong, not a resolver test result. Its EXIT trap ran and `sudo ip netns list` showed no leftover namespace. I am updating the temporary runner to select a binary by the test name and require exactly one passing test before reporting the namespace gate.
Author
Owner

Finding: the corrected selector chose dns-75ff6ac5a04b8625 and executed exactly one ignored namespace test, but it failed before resolver setup at tests/dns.rs:252. The temporary runner passed INFRATERAL_DNS_TEST_PARENT_NETNS; the test expects INFRATERNAL_DNS_TEST_PARENT_NETNS (the runner omitted the second n). Cleanup was verified again and no namespace remained. I am correcting the runner's environment variable spelling and rerunning the same test.

Finding: the corrected selector chose `dns-75ff6ac5a04b8625` and executed exactly one ignored namespace test, but it failed before resolver setup at `tests/dns.rs:252`. The temporary runner passed `INFRATERAL_DNS_TEST_PARENT_NETNS`; the test expects `INFRATERNAL_DNS_TEST_PARENT_NETNS` (the runner omitted the second `n`). Cleanup was verified again and no namespace remained. I am correcting the runner's environment variable spelling and rerunning the same test.
Author
Owner

Finding: the corrected root runner selected the intended DNS integration binary and the ignored namespace test itself passed: running 1 test, test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok, 1 passed; 0 failed. The temporary runner exited 1 because its summary assertion incorrectly expected 0 filtered out; the exact-name filter correctly reports 8 filtered out. Namespace cleanup was verified. I am changing only the temporary assertion to accept a filtered-test count and rerunning for a clean wrapper exit.

Finding: the corrected root runner selected the intended DNS integration binary and the ignored namespace test itself passed: `running 1 test`, `test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok`, `1 passed; 0 failed`. The temporary runner exited 1 because its summary assertion incorrectly expected `0 filtered out`; the exact-name filter correctly reports `8 filtered out`. Namespace cleanup was verified. I am changing only the temporary assertion to accept a filtered-test count and rerunning for a clean wrapper exit.
Author
Owner

Finding resolved: the root-only namespace test passed on dev-02 through ssh cua + sudo + build-slot, using the final-head test binary selected by the exact test name. Output: running 1 test; test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok; test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 8 filtered out; finished in 0.21s. The wrapper exited 0 and verified no j7-mesh-dns-* namespace remained.

Finding resolved: the root-only namespace test passed on dev-02 through `ssh cua` + sudo + `build-slot`, using the final-head test binary selected by the exact test name. Output: `running 1 test`; `test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok`; `test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 8 filtered out; finished in 0.21s`. The wrapper exited 0 and verified no `j7-mesh-dns-*` namespace remained.
Author
Owner

Status: dev-02 feature-on all-targets and the corrected root-only network-namespace test have passed on ae1004a53fd938e8f3e0efde7d6712846be179f7. The final x86 scripts/verify fast remains active in compute01's build-slot queue (all 4 slots busy; no gate output yet). Its log is /home/claude/.cache/codex-scratch/j7-mesh-dns/evidence/verify-fast-final.log; the queued command is /home/claude/.cache/codex-scratch/j7-mesh-dns/evidence/verify-fast-final.sh.

Status: dev-02 feature-on all-targets and the corrected root-only network-namespace test have passed on `ae1004a53fd938e8f3e0efde7d6712846be179f7`. The final x86 `scripts/verify fast` remains active in compute01's `build-slot` queue (all 4 slots busy; no gate output yet). Its log is `/home/claude/.cache/codex-scratch/j7-mesh-dns/evidence/verify-fast-final.log`; the queued command is `/home/claude/.cache/codex-scratch/j7-mesh-dns/evidence/verify-fast-final.sh`.
Author
Owner

30-minute progress: branch head remains ae1004a53fd938e8f3e0efde7d6712846be179f7; dev-02 feature-on all-targets and the root namespace test are green. Compute01 is reachable with 55 GiB free, but the final scripts/verify fast is still waiting in build-slot (all 4 slots busy), with no gate output yet. The active command and log remain /home/claude/.cache/codex-scratch/j7-mesh-dns/evidence/verify-fast-final.sh and verify-fast-final.log.

30-minute progress: branch head remains `ae1004a53fd938e8f3e0efde7d6712846be179f7`; dev-02 feature-on all-targets and the root namespace test are green. Compute01 is reachable with 55 GiB free, but the final `scripts/verify fast` is still waiting in `build-slot` (all 4 slots busy), with no gate output yet. The active command and log remain `/home/claude/.cache/codex-scratch/j7-mesh-dns/evidence/verify-fast-final.sh` and `verify-fast-final.log`.
Author
Owner

Status update: compute01 is reachable after its reboot, but the final x86 scripts/verify fast remains queued in ~/infraternal-ops/build-slot. The original process is still active against head ae1004a53fd938e8f3e0efde7d6712846be179f7; it has waited over 50 minutes and all four slot locks were confirmed busy. The gate log still contains only the initial queue message, with no exit status yet. The dev-02 feature-on all-targets suite and root-only namespace test passed on this head, as recorded above. I am keeping the queued x86 run active and will report its D243 row and full output when it completes.

Status update: compute01 is reachable after its reboot, but the final x86 `scripts/verify fast` remains queued in `~/infraternal-ops/build-slot`. The original process is still active against head `ae1004a53fd938e8f3e0efde7d6712846be179f7`; it has waited over 50 minutes and all four slot locks were confirmed busy. The gate log still contains only the initial queue message, with no exit status yet. The dev-02 feature-on all-targets suite and root-only namespace test passed on this head, as recorded above. I am keeping the queued x86 run active and will report its D243 row and full output when it completes.
Author
Owner

One-hour queue update: the final x86 scripts/verify fast process remains active on compute01 and is still waiting in build-slot. At the last check it had waited 1:00:37; all four slot locks were busy, while the gate log still contained only the initial queue message and no exit file. The branch head and dev-02 test results are unchanged. I have kept the original queued run active; the x86 size row and full fast gate remain pending until a slot becomes available.

One-hour queue update: the final x86 `scripts/verify fast` process remains active on compute01 and is still waiting in `build-slot`. At the last check it had waited 1:00:37; all four slot locks were busy, while the gate log still contained only the initial queue message and no exit file. The branch head and dev-02 test results are unchanged. I have kept the original queued run active; the x86 size row and full fast gate remain pending until a slot becomes available.
Author
Owner

90-minute queue update: compute01 is reachable, but the final x86 scripts/verify fast remains active and waiting in build-slot. It has waited 1:30:39; all four slots are still occupied, although lock holders have rotated as other jobs progress. The J7 log still contains only the initial wait message, and no exit file exists. The implementation and dev-02 DNS tests are unchanged; the D243 x86 row and final gate output are pending slot availability.

90-minute queue update: compute01 is reachable, but the final x86 `scripts/verify fast` remains active and waiting in `build-slot`. It has waited 1:30:39; all four slots are still occupied, although lock holders have rotated as other jobs progress. The J7 log still contains only the initial wait message, and no exit file exists. The implementation and dev-02 DNS tests are unchanged; the D243 x86 row and final gate output are pending slot availability.
Author
Owner

Two-hour queue update: compute01 remains reachable. The original final x86 scripts/verify fast process is still active, waiting in build-slot at 2:00:29. All four slots remain occupied; the log still contains only the initial wait line and no exit status exists. No code or branch changes have been made since the queued run was started. The D243 size row and final gate result remain pending admission to a slot.

Two-hour queue update: compute01 remains reachable. The original final x86 `scripts/verify fast` process is still active, waiting in `build-slot` at 2:00:29. All four slots remain occupied; the log still contains only the initial wait line and no exit status exists. No code or branch changes have been made since the queued run was started. The D243 size row and final gate result remain pending admission to a slot.
Author
Owner

Two-and-a-half-hour queue update: compute01 is reachable and the final x86 scripts/verify fast process remains active in build-slot (2:29:15 waited at last check). All four slot locks are occupied by other jobs. The J7 gate log has no output beyond the initial wait message, and no exit status exists. The issue remains open; x86 static-size and final gate results are pending slot availability.

Two-and-a-half-hour queue update: compute01 is reachable and the final x86 `scripts/verify fast` process remains active in `build-slot` (2:29:15 waited at last check). All four slot locks are occupied by other jobs. The J7 gate log has no output beyond the initial wait message, and no exit status exists. The issue remains open; x86 static-size and final gate results are pending slot availability.
Author
Owner

Finding from the compute01 fast gate: replay_size_review failed before the fixture starts because scripts/agent_fixture.py::fixture_state calculated the fallback .upstream AF_UNIX path as exactly 108 bytes, exceeding the 107-byte pathname limit. The INFRATERNAL_AGENT_TEST_STATE_ROOT/INFRATERNAL_TEST_STATE_ROOT overrides were unset. Evidence: the identical failure reproduces using agent_fixture.py extracted from unmodified origin/dev; git diff --exit-code origin/dev -- scripts/agent_fixture.py scripts/verify-agent-replay-size-review.py is clean. Setting the correct INFRATERNAL_AGENT_TEST_STATE_ROOT to a /tmp temporary root gives an 83-byte socket path. This is an environment-path issue in unchanged test infrastructure, not a J7 source change. The original fast gate is still running; if its exit is nonzero, I will rerun the full gate with the short test root set and retain both logs.

Finding from the compute01 fast gate: `replay_size_review` failed before the fixture starts because `scripts/agent_fixture.py::fixture_state` calculated the fallback `.upstream` AF_UNIX path as exactly 108 bytes, exceeding the 107-byte pathname limit. The `INFRATERNAL_AGENT_TEST_STATE_ROOT`/`INFRATERNAL_TEST_STATE_ROOT` overrides were unset. Evidence: the identical failure reproduces using `agent_fixture.py` extracted from unmodified `origin/dev`; `git diff --exit-code origin/dev -- scripts/agent_fixture.py scripts/verify-agent-replay-size-review.py` is clean. Setting the correct `INFRATERNAL_AGENT_TEST_STATE_ROOT` to a `/tmp` temporary root gives an 83-byte socket path. This is an environment-path issue in unchanged test infrastructure, not a J7 source change. The original fast gate is still running; if its exit is nonzero, I will rerun the full gate with the short test root set and retain both logs.
Author
Owner

Finding: the first compute01 x86_64 static release check on head ae1004a53fd938e8f3e0efde7d6712846be179f7 reports the D243 row verbatim: PT_INTERP: absent; size: 21431472 bytes; cap: 21431472 bytes; delta: +0 bytes. The full scripts/verify fast continues through its remaining static target. It has one unrelated default fixture-path failure documented above; after this run completes I will rerun with the short INFRATERNAL_AGENT_TEST_STATE_ROOT so the final full gate can complete cleanly.

Finding: the first compute01 x86_64 static release check on head `ae1004a53fd938e8f3e0efde7d6712846be179f7` reports the D243 row verbatim: `PT_INTERP: absent; size: 21431472 bytes; cap: 21431472 bytes; delta: +0 bytes`. The full `scripts/verify fast` continues through its remaining static target. It has one unrelated default fixture-path failure documented above; after this run completes I will rerun with the short `INFRATERNAL_AGENT_TEST_STATE_ROOT` so the final full gate can complete cleanly.
Author
Owner

First full compute01 gate completed on head ae1004a53fd938e8f3e0efde7d6712846be179f7 with exit 1. The summary table reports fmt, VT fmt, CLI compile-out, clippy, SSH runtime helper, real-binary fixture paths, SSH inode reuse, shim per-user, cargo-deny, architecture, and static builds as PASS; test is FAIL (101) solely due to the replay-size fixture path error documented above. Both static rows pass their exact D243 caps: x86_64 PT_INTERP: absent; size: 21431472 bytes; cap: 21431472 bytes; delta: +0 bytes; aarch64 PT_INTERP: absent; size: 19605896 bytes; cap: 19605896 bytes; delta: +0 bytes. I have saved the full first-run log and will rerun scripts/verify fast with the verified short INFRATERNAL_AGENT_TEST_STATE_ROOT so the fixture test and complete gate are checked cleanly.

First full compute01 gate completed on head `ae1004a53fd938e8f3e0efde7d6712846be179f7` with exit `1`. The summary table reports `fmt`, VT fmt, CLI compile-out, clippy, SSH runtime helper, real-binary fixture paths, SSH inode reuse, shim per-user, cargo-deny, architecture, and static builds as PASS; `test` is FAIL (101) solely due to the replay-size fixture path error documented above. Both static rows pass their exact D243 caps: x86_64 `PT_INTERP: absent; size: 21431472 bytes; cap: 21431472 bytes; delta: +0 bytes`; aarch64 `PT_INTERP: absent; size: 19605896 bytes; cap: 19605896 bytes; delta: +0 bytes`. I have saved the full first-run log and will rerun `scripts/verify fast` with the verified short `INFRATERNAL_AGENT_TEST_STATE_ROOT` so the fixture test and complete gate are checked cleanly.
Author
Owner

Gate finding on compute01: the second scripts/verify fast run passed every summary gate except the workspace test stage. It reported 146 passed, 3 failed, 20 ignored; all three failures were in the unchanged native_gone_review_tests and failed creating AF_UNIX sockets with path must be shorter than SUN_LEN. The wrapper had set INFRATERNAL_AGENT_TEST_STATE_ROOT for Python fixtures but omitted INFRATERNAL_TEST_STATE_ROOT, which Rust's crates/infraternal-agent/src/lib.rs::test_state_root reads. Both static rows passed exactly at their D243 caps (x86_64 21,431,472 bytes; aarch64 19,605,896 bytes). I am rerunning the full gate with both variables set to a short /tmp/j7.* root.

Gate finding on compute01: the second `scripts/verify fast` run passed every summary gate except the workspace test stage. It reported 146 passed, 3 failed, 20 ignored; all three failures were in the unchanged native_gone_review_tests and failed creating AF_UNIX sockets with `path must be shorter than SUN_LEN`. The wrapper had set INFRATERNAL_AGENT_TEST_STATE_ROOT for Python fixtures but omitted INFRATERNAL_TEST_STATE_ROOT, which Rust's `crates/infraternal-agent/src/lib.rs::test_state_root` reads. Both static rows passed exactly at their D243 caps (x86_64 21,431,472 bytes; aarch64 19,605,896 bytes). I am rerunning the full gate with both variables set to a short `/tmp/j7.*` root.
Author
Owner

Compute01 update: the corrected final scripts/verify fast wrapper is queued through ~/infraternal-ops/build-slot (all four slots occupied; queue position 12 at 00:54 UTC). It sets both INFRATERNAL_AGENT_TEST_STATE_ROOT and INFRATERNAL_TEST_STATE_ROOT to a short /tmp/j7.* directory. The exact D243 rows already passed in the previous run; the rerun is required to clear the Rust workspace test stage.

Compute01 update: the corrected final `scripts/verify fast` wrapper is queued through `~/infraternal-ops/build-slot` (all four slots occupied; queue position 12 at 00:54 UTC). It sets both `INFRATERNAL_AGENT_TEST_STATE_ROOT` and `INFRATERNAL_TEST_STATE_ROOT` to a short `/tmp/j7.*` directory. The exact D243 rows already passed in the previous run; the rerun is required to clear the Rust workspace test stage.
Author
Owner

Queue progress: the initial build-slot message showed position 12, but the live FIFO queue has since advanced; the gate's ticket (PID 1581176) is now fourth. Four slot locks are still occupied, so the corrected full verification remains queued and no new gate result is available yet.

Queue progress: the initial build-slot message showed position 12, but the live FIFO queue has since advanced; the gate's ticket (PID 1581176) is now fourth. Four slot locks are still occupied, so the corrected full verification remains queued and no new gate result is available yet.
Author
Owner

Compute01 finding: the second full scripts/verify fast run still exited 1 in the workspace test stage (7 failures; D243 static rows both passed at the exact caps). The failures are in restart image fixtures: PermissionDenied during candidate admission/exec and CrossesDevices during atomic rename. findmnt confirms /tmp is a separate tmpfs while /home/claude is ext4. The short /tmp/j7.* root therefore gave those tests different filesystem semantics from the worktree. I am retrying with both test-state variables set to a short root under $HOME/.cache/codex-scratch so the Unix socket paths stay short and fixtures share the worktree filesystem.

Compute01 finding: the second full `scripts/verify fast` run still exited 1 in the workspace test stage (7 failures; D243 static rows both passed at the exact caps). The failures are in restart image fixtures: `PermissionDenied` during candidate admission/exec and `CrossesDevices` during atomic rename. `findmnt` confirms `/tmp` is a separate tmpfs while `/home/claude` is ext4. The short `/tmp/j7.*` root therefore gave those tests different filesystem semantics from the worktree. I am retrying with both test-state variables set to a short root under `$HOME/.cache/codex-scratch` so the Unix socket paths stay short and fixtures share the worktree filesystem.
Author
Owner

Corrected-root retry: the new wrapper now uses a short root under $HOME/.cache/codex-scratch, which is on the same ext4 filesystem as the worktree. It first runs the restart-focused tests, then scripts/verify fast in the same slot if they pass. It is queued at position 12 because all four compute01 slots are occupied. The prior /tmp run has been retained as evidence of the filesystem mismatch.

Corrected-root retry: the new wrapper now uses a short root under `$HOME/.cache/codex-scratch`, which is on the same ext4 filesystem as the worktree. It first runs the restart-focused tests, then `scripts/verify fast` in the same slot if they pass. It is queued at position 12 because all four compute01 slots are occupied. The prior `/tmp` run has been retained as evidence of the filesystem mismatch.
Author
Owner

Progress after the filesystem-root correction: branch mesh-dns remains clean and pushed at ae1004a53fd938e8f3e0efde7d6712846be179f7. Both D243 static rows passed at their exact caps in the prior full run. The valid retry uses a short $HOME/.cache/codex-scratch/j7.* state root and runs the restart-focused tests followed by scripts/verify fast under one build-slot reservation. Its ticket has waited about 34 minutes and is fourth in the FIFO queue; four slots remain occupied. The issue stays open.

Progress after the filesystem-root correction: branch `mesh-dns` remains clean and pushed at `ae1004a53fd938e8f3e0efde7d6712846be179f7`. Both D243 static rows passed at their exact caps in the prior full run. The valid retry uses a short `$HOME/.cache/codex-scratch/j7.*` state root and runs the restart-focused tests followed by `scripts/verify fast` under one build-slot reservation. Its ticket has waited about 34 minutes and is fourth in the FIFO queue; four slots remain occupied. The issue stays open.
Author
Owner

J7 dev-02 evidence for the Linux DNS spike at ae1004a53f: feature-on-all-targets.log records the feature-enabled test run, and netns-final.log records the root-only isolated UDP namespace test and cleanup. These were run on dev-02 while compute01 was down.

J7 dev-02 evidence for the Linux DNS spike at ae1004a53fd938e8f3e0efde7d6712846be179f7: feature-on-all-targets.log records the feature-enabled test run, and netns-final.log records the root-only isolated UDP namespace test and cleanup. These were run on dev-02 while compute01 was down.
Author
Owner

Compute01 diagnostic result: with both INFRATERNAL_TEST_STATE_ROOT and INFRATERNAL_AGENT_TEST_STATE_ROOT set to a short $HOME/.cache/codex-scratch/j7.* root on ext4, the restart-focused tests passed (28 passed, 0 failed, 1 ignored). This confirms the prior /tmp run failed because its test root was on a separate tmpfs. The full scripts/verify fast is now running in the same slot; attached restart-tests-home-root.log.

Compute01 diagnostic result: with both `INFRATERNAL_TEST_STATE_ROOT` and `INFRATERNAL_AGENT_TEST_STATE_ROOT` set to a short `$HOME/.cache/codex-scratch/j7.*` root on ext4, the restart-focused tests passed (28 passed, 0 failed, 1 ignored). This confirms the prior `/tmp` run failed because its test root was on a separate tmpfs. The full `scripts/verify fast` is now running in the same slot; attached restart-tests-home-root.log.
Author
Owner

Finished J7 on mesh-dns, head ae1004a53fd938e8f3e0efde7d6712846be179f7 (pushed; clean; based on origin/dev 57491e918). The corrected compute01 run used a short state root under $HOME/.cache/codex-scratch on the same filesystem as the worktree.

The restart-focused test passed: 28 passed; 0 failed; 1 ignored. scripts/verify fast on compute01 exited 0. Full output is attached.

Verbatim final gate summary:

+---------------------------+--------------+
| Rust gate                 | Result       |
+---------------------------+--------------+
| fmt                       | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| real-binary fixture paths | PASS         |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+
PT_INTERP: absent; size: 21431472 bytes; cap: 21431472 bytes; delta: +0 bytes
PT_INTERP: absent; size: 19605896 bytes; cap: 19605896 bytes; delta: +0 bytes
x86_64-unknown-linux-musl: exact 21431472-byte cap PASS; 21431473 bytes rejected PASS; smaller artifact requires ratchet PASS
aarch64-unknown-linux-musl: exact 19605896-byte cap PASS; 19605897 bytes rejected PASS; smaller artifact requires ratchet PASS

The default-off mesh-dns-spike remains a prototype; the DNS foundation library choice and denied-peer name visibility remain owner questions. Issue stays open.

Finished J7 on `mesh-dns`, head `ae1004a53fd938e8f3e0efde7d6712846be179f7` (pushed; clean; based on `origin/dev` `57491e918`). The corrected compute01 run used a short state root under `$HOME/.cache/codex-scratch` on the same filesystem as the worktree. The restart-focused test passed: `28 passed; 0 failed; 1 ignored`. `scripts/verify fast` on compute01 exited 0. Full output is attached. Verbatim final gate summary: ``` +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | fmt | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | static builds | PASS | +---------------------------+--------------+ PT_INTERP: absent; size: 21431472 bytes; cap: 21431472 bytes; delta: +0 bytes PT_INTERP: absent; size: 19605896 bytes; cap: 19605896 bytes; delta: +0 bytes x86_64-unknown-linux-musl: exact 21431472-byte cap PASS; 21431473 bytes rejected PASS; smaller artifact requires ratchet PASS aarch64-unknown-linux-musl: exact 19605896-byte cap PASS; 19605897 bytes rejected PASS; smaller artifact requires ratchet PASS ``` The default-off `mesh-dns-spike` remains a prototype; the DNS foundation library choice and denied-peer name visibility remain owner questions. Issue stays open.
Author
Owner

Author mesh review checklist completed for the DNS slice: the default-off resolver binds only the mesh IPv4 high port; labels are validated and case-folded, duplicates are rejected, and only accepted versioned snapshots replace records. Coordinator loss retains the last accepted snapshot; node down drops the resolver and joins its worker; oversized UDP datagrams are detected and dropped. Existing route, firewall and journal transactions are unchanged, and no host resolver configuration is written. simple-dns remains isolated behind mesh-dns-spike; the foundational library choice remains with the owner. No checklist finding requires another code change.

Author mesh review checklist completed for the DNS slice: the default-off resolver binds only the mesh IPv4 high port; labels are validated and case-folded, duplicates are rejected, and only accepted versioned snapshots replace records. Coordinator loss retains the last accepted snapshot; node down drops the resolver and joins its worker; oversized UDP datagrams are detected and dropped. Existing route, firewall and journal transactions are unchanged, and no host resolver configuration is written. `simple-dns` remains isolated behind `mesh-dns-spike`; the foundational library choice remains with the owner. No checklist finding requires another code change.
Author
Owner

Owner decisions (kayg, 2026-11-01, relayed by the orchestrator):

  1. Names: <hostname>.internal by default — the real hostname, no unnamed-<id> projection in DNS.
  2. Who sees a name: DNS publishes a peer's name only to peers allowed to reach it (option c). This replaces hiding: the access policy gates visibility. Revoking access removes the name for that asker.
  3. Suffix: .internal by default; the user can change the domain. With a custom domain, the reverse proxy can optionally obtain a TLS certificate for those names (DNS-01, D316).
  4. Library: not final yet. Owner asked whether simple-dns also gives AdGuard-style filter lists — it does not (it is only a packet parser/encoder). Start small (simple-dns for the mesh zone, still default-off) and expand; a research job now measures filtering options (list engine: adblock crate vs our own DNS-subset matcher; upstream forwarding incl. DoH/DoT: hickory-resolver vs own forwarder on simple-dns) so the owner can choose with numbers.

Follow-up for the mesh-dns slice: implement 2 (reachability-gated publication) and 1 (real hostnames) before merge.

**Owner decisions (kayg, 2026-11-01, relayed by the orchestrator):** 1. **Names:** `<hostname>.internal` by default — the real hostname, no `unnamed-<id>` projection in DNS. 2. **Who sees a name:** DNS publishes a peer's name **only to peers allowed to reach it** (option c). This replaces hiding: the access policy gates visibility. Revoking access removes the name for that asker. 3. **Suffix:** `.internal` by default; the user can change the domain. With a custom domain, the reverse proxy can optionally obtain a TLS certificate for those names (DNS-01, D316). 4. **Library:** not final yet. Owner asked whether simple-dns also gives AdGuard-style filter lists — it does not (it is only a packet parser/encoder). Start small (simple-dns for the mesh zone, still default-off) and expand; a research job now measures filtering options (list engine: `adblock` crate vs our own DNS-subset matcher; upstream forwarding incl. DoH/DoT: `hickory-resolver` vs own forwarder on simple-dns) so the owner can choose with numbers. Follow-up for the mesh-dns slice: implement 2 (reachability-gated publication) and 1 (real hostnames) before merge.
Author
Owner

R1 research started on research-dns-filtering, base f4b61cd6c. Isolated worktree /home/kayg/Developer/Worktrees/infraternal-r1-dns. Measuring filtering and forwarding candidates only; no foundation choice. Builds: compute01 through build-slot; measurements: perf-01 through perf-run. Harness target path will be /home/claude/worktrees/r1-dns/target (one shared target for this job).

R1 research started on `research-dns-filtering`, base `f4b61cd6c`. Isolated worktree `/home/kayg/Developer/Worktrees/infraternal-r1-dns`. Measuring filtering and forwarding candidates only; no foundation choice. Builds: compute01 through build-slot; measurements: perf-01 through perf-run. Harness target path will be `/home/claude/worktrees/r1-dns/target` (one shared target for this job).
Author
Owner

Owner clarification (kayg, 2026-11-01): the DNS rule is "a user/machine should be able to look up other users/machines they have access to" — publication is gated by the mesh access grants/policies (machine→machine, user→machine, group/tag grants; one-way grants resolve one way only), and the grants/policies must be verified to cover this scenario. No separate DNS visibility rule. J48 implements and audits for gaps; anything not expressible in today's policy model comes back as an owner question.

**Owner clarification (kayg, 2026-11-01):** the DNS rule is "a user/machine should be able to look up other users/machines they have access to" — publication is gated by the mesh access grants/policies (machine→machine, user→machine, group/tag grants; one-way grants resolve one way only), and the grants/policies must be verified to cover this scenario. No separate DNS visibility rule. J48 implements and audits for gaps; anything not expressible in today's policy model comes back as an owner question.
Author
Owner

Starting J48 on branch mesh-dns, worktree /home/kayg/Developer/Worktrees/infraternal-mesh-dns, rebasing the J7 head ae1004a53fd938e8f3e0efde7d6712846be179f7 onto origin/dev f4b61cd6c3f52078cad7573c5367d7a6e75f84f0. I will audit the existing directional grants/policies as the single source for WireGuard and DNS, add failing tests for denied/granted/revoked visibility and real hostnames, and verify machine, user, tag/group, and one-way cases are expressible before implementing. I will keep mesh-dns-spike default-off and report any policy-model gaps on #266/#173.

Starting J48 on branch `mesh-dns`, worktree `/home/kayg/Developer/Worktrees/infraternal-mesh-dns`, rebasing the J7 head `ae1004a53fd938e8f3e0efde7d6712846be179f7` onto `origin/dev` `f4b61cd6c3f52078cad7573c5367d7a6e75f84f0`. I will audit the existing directional grants/policies as the single source for WireGuard and DNS, add failing tests for denied/granted/revoked visibility and real hostnames, and verify machine, user, tag/group, and one-way cases are expressible before implementing. I will keep `mesh-dns-spike` default-off and report any policy-model gaps on #266/#173.
Author
Owner

Progress: the clean mesh-dns branch is rebased onto origin/dev f4b61cd6c3f52078cad7573c5367d7a6e75f84f0 and pushed at 85a08d106. The scope is unchanged. I am tracing the coordinator’s outbound policy view and the accepted-chunk apply path before writing RED tests for per-receiver filtering and revocation.

Progress: the clean `mesh-dns` branch is rebased onto `origin/dev` `f4b61cd6c3f52078cad7573c5367d7a6e75f84f0` and pushed at `85a08d106`. The scope is unchanged. I am tracing the coordinator’s outbound policy view and the accepted-chunk apply path before writing RED tests for per-receiver filtering and revocation.
Author
Owner

R1 source findings before measurement:

  • simple-dns is a wire parser/encoder, not a list engine or upstream resolver. Filtering and encrypted forwarding are separate choices.
  • Pinned realistic list inputs downloaded 2026-11-01: AdGuard DNS filter 186,236 non-comment rule lines, OISD big 232,023, StevenBlack hosts 76,750; total 495,009 before deduplication (12,145,145 input bytes plus separators). Hashes and exact URLs are in the harness source manifest. The AdGuard list contains 212 exception lines and five $badfilter modifiers, so dropping exceptions/modifiers is a correctness issue, not only reduced coverage.
  • adblock 0.13.5 is MPL-2.0. MPL §1.12 explicitly includes GNU AGPL v3 among Secondary Licenses; §3.3 permits the combined AGPL work when the MPL code is not marked incompatible. The packaged source has standard MPL headers and no attached Exhibit B exclusion outside the full license text. The presence of Exhibit B inside the license is not an exclusion (Mozilla FAQ Q25). Preserve MPL notices and provide that source under MPL plus the applicable secondary-license route; this is not a claim that arbitrary MPL code is always AGPL-compatible. Sources: https://www.mozilla.org/en-US/MPL/2.0/ and https://www.mozilla.org/en-US/MPL/2.0/FAQ/ .
  • A third candidate, utilvo-shield-filters 1.0.0, is MIT and recently maintained (repository not archived, pushed 2026-10-20). Its source parses ||domain^ but drops @@ and does not implement $important, hosts, or DNS-specific modifiers. Measuring it as a small alternative, not treating it as a complete DNS filter engine. dgaard-engine 0.2.0 (Apache-2.0, repo pushed 2026-10-29) is another maintained engine, but its heuristic/scoring pipeline is broader than this syntax-focused comparison; blocklist 1.0.0 bakes categories at build time and cannot reload arbitrary subscription lists without rebuilding.

Harness builds remain queued through compute01 build-slot (four slots occupied). No production dependencies or decisions changed.

R1 source findings before measurement: - `simple-dns` is a wire parser/encoder, not a list engine or upstream resolver. Filtering and encrypted forwarding are separate choices. - Pinned realistic list inputs downloaded 2026-11-01: AdGuard DNS filter 186,236 non-comment rule lines, OISD big 232,023, StevenBlack hosts 76,750; total 495,009 before deduplication (12,145,145 input bytes plus separators). Hashes and exact URLs are in the harness source manifest. The AdGuard list contains 212 exception lines and five `$badfilter` modifiers, so dropping exceptions/modifiers is a correctness issue, not only reduced coverage. - `adblock` 0.13.5 is MPL-2.0. MPL §1.12 explicitly includes GNU AGPL v3 among Secondary Licenses; §3.3 permits the combined AGPL work when the MPL code is not marked incompatible. The packaged source has standard MPL headers and no attached Exhibit B exclusion outside the full license text. The presence of Exhibit B *inside the license* is not an exclusion (Mozilla FAQ Q25). Preserve MPL notices and provide that source under MPL plus the applicable secondary-license route; this is not a claim that arbitrary MPL code is always AGPL-compatible. Sources: https://www.mozilla.org/en-US/MPL/2.0/ and https://www.mozilla.org/en-US/MPL/2.0/FAQ/ . - A third candidate, `utilvo-shield-filters` 1.0.0, is MIT and recently maintained (repository not archived, pushed 2026-10-20). Its source parses `||domain^` but drops `@@` and does not implement `$important`, hosts, or DNS-specific modifiers. Measuring it as a small alternative, not treating it as a complete DNS filter engine. `dgaard-engine` 0.2.0 (Apache-2.0, repo pushed 2026-10-29) is another maintained engine, but its heuristic/scoring pipeline is broader than this syntax-focused comparison; `blocklist` 1.0.0 bakes categories at build time and cannot reload arbitrary subscription lists without rebuilding. Harness builds remain queued through compute01 build-slot (four slots occupied). No production dependencies or decisions changed.
Author
Owner

Audit finding: the coordinator already has node-to-node and tag-to-tag arrows, and configuration::compile derives each receiver's PeerPolicy.outbound from that directional view plus the configured default. The source model expands tag endpoints to member node IDs and Arrow::both_ways=false is directional. D164 explicitly defers user endpoints until devices arrive in stage 2; Endpoint::User is rejected by Arrow::validate (crates/infraternal-mesh/src/rules.rs:78-86), and the Mesh API also rejects it (crates/plugins/mesh/src/api.rs:463-466,1804-1808). Node inventory has no user owner/membership field. I will preserve this boundary, test it, and cover node/tag and one-way grants in DNS.

Owner question: should user-to-machine DNS visibility wait for a separate stage-2 user-device grant design, or is there an owner-approved user identity/membership model to use now? J48 will not invent one.

Audit finding: the coordinator already has node-to-node and tag-to-tag arrows, and `configuration::compile` derives each receiver's `PeerPolicy.outbound` from that directional view plus the configured default. The source model expands tag endpoints to member node IDs and `Arrow::both_ways=false` is directional. D164 explicitly defers user endpoints until devices arrive in stage 2; `Endpoint::User` is rejected by `Arrow::validate` (`crates/infraternal-mesh/src/rules.rs:78-86`), and the Mesh API also rejects it (`crates/plugins/mesh/src/api.rs:463-466,1804-1808`). Node inventory has no user owner/membership field. I will preserve this boundary, test it, and cover node/tag and one-way grants in DNS. Owner question: should user-to-machine DNS visibility wait for a separate stage-2 user-device grant design, or is there an owner-approved user identity/membership model to use now? J48 will not invent one.
Author
Owner

TDD RED (dev-02-std via build-slot, nix develop, head d4f2fa3cc322b16995896455c8864df9b2df2157): cargo test --locked -p infraternal-plugin-mesh --features server,mesh-dns-spike --test configuration -j3 compiled and reported test result: FAILED. 7 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.12s. The four expected failures are denied-peer NXDOMAIN/name absence, reverse direction of a node grant, reverse direction of a tag grant, and grant revocation removing the name from the next snapshot. The Endpoint::User rejection test and existing configuration tests pass. This confirms configuration::compile unconditionally inserts member.node.host; a revoked name also needs a chunk removal because the denied peer key remains in the WireGuard peer map. Log: /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/red-feature-on.log on dev-02-std.

TDD RED (dev-02-std via `build-slot`, `nix develop`, head `d4f2fa3cc322b16995896455c8864df9b2df2157`): `cargo test --locked -p infraternal-plugin-mesh --features server,mesh-dns-spike --test configuration -j3` compiled and reported `test result: FAILED. 7 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.12s`. The four expected failures are denied-peer NXDOMAIN/name absence, reverse direction of a node grant, reverse direction of a tag grant, and grant revocation removing the name from the next snapshot. The `Endpoint::User` rejection test and existing configuration tests pass. This confirms `configuration::compile` unconditionally inserts `member.node.host`; a revoked name also needs a chunk removal because the denied peer key remains in the WireGuard peer map. Log: `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/red-feature-on.log` on dev-02-std.
Author
Owner

R1 progress: sources/licence review and all isolated harnesses are prepared; compute01's original build-slot ticket is now first in FIFO but all four slots are occupied. No compiler for this job has run yet. The queued sequence builds all static candidates, then runs the required full fast gate using the same single target /home/claude/worktrees/r1-dns/target. Performance runner and pinned list inputs are staged on perf-01; no perf workload or fixture server is running there yet.

Input syntax audit predicts the strict subset can accept 494,123 entries (435,154 unique normalized entries), rejecting 886 of the 495,009 input rule lines. These are syntax counts, not evidence of equivalent blocking: the rejected rules include wildcard/regex forms and $badfilter. The measured harness will report its own counts and explicit precedence/coverage probes. Code and the staged owner questions are in /home/kayg/Developer/Worktrees/infraternal-r1-dns/research/dns-filtering/.

R1 progress: sources/licence review and all isolated harnesses are prepared; compute01's original build-slot ticket is now first in FIFO but all four slots are occupied. No compiler for this job has run yet. The queued sequence builds all static candidates, then runs the required full fast gate using the same single target `/home/claude/worktrees/r1-dns/target`. Performance runner and pinned list inputs are staged on perf-01; no perf workload or fixture server is running there yet. Input syntax audit predicts the strict subset can accept 494,123 entries (435,154 unique normalized entries), rejecting 886 of the 495,009 input rule lines. These are syntax counts, not evidence of equivalent blocking: the rejected rules include wildcard/regex forms and `$badfilter`. The measured harness will report its own counts and explicit precedence/coverage probes. Code and the staged owner questions are in `/home/kayg/Developer/Worktrees/infraternal-r1-dns/research/dns-filtering/`.
Author
Owner

R1 first measured finding (perf-01, perf-run, three fresh processes per engine; static executables built through compute01 build-slot):

Prototype Static bytes (delta vs 392,632 B baseline) Resident RSS after source text release Lookup p50 / p99 (median of three runs) Compile / full rebuild (median) Old + new resident snapshots
Straight BTreeMap reversed-label trie 453,592 (+60,960) 154,516 KiB 371 / 1,623 ns 511.9 / 552.5 ms 307,976 KiB
utilvo-shield-filters 1.0.0 453,528 (+60,896) 25,856 KiB 290 / 771 ns 104.6 / 109.4 ms 50,648 KiB

These are not equivalent coverage: the trie accepts 494,123 expanded rule entries and rejects 895 entries (the earlier source audit counted 886 lines; multi-host line expansion accounts for the difference), while utilvo loads only 358,779 unique domains and drops exceptions/hosts/plain rules. Explicit probes show utilvo blocks client-scoped and third-party rules unconditionally and does not honor badfilter. It is not recommended as a full engine despite its small measurements.

The naive trie is fast but allocates heavily: about 151 MiB per DNS process, about 301 MiB while both snapshots coexist. I am measuring a sorted compact index with the same parser/precedence logic before recommending an owned engine. The initial adblock adapter build failed because 0.13.5 changed Request::new and BlockerResult; source-verified corrections are queued. No candidate is selected. Perf receipt attached; full comparison follows after the remaining builds and gate.

R1 first measured finding (perf-01, perf-run, three fresh processes per engine; static executables built through compute01 build-slot): | Prototype | Static bytes (delta vs 392,632 B baseline) | Resident RSS after source text release | Lookup p50 / p99 (median of three runs) | Compile / full rebuild (median) | Old + new resident snapshots | |---|---:|---:|---:|---:|---:| | Straight BTreeMap reversed-label trie | 453,592 (+60,960) | 154,516 KiB | 371 / 1,623 ns | 511.9 / 552.5 ms | 307,976 KiB | | utilvo-shield-filters 1.0.0 | 453,528 (+60,896) | 25,856 KiB | 290 / 771 ns | 104.6 / 109.4 ms | 50,648 KiB | These are not equivalent coverage: the trie accepts 494,123 expanded rule entries and rejects 895 entries (the earlier source audit counted 886 *lines*; multi-host line expansion accounts for the difference), while utilvo loads only 358,779 unique domains and drops exceptions/hosts/plain rules. Explicit probes show utilvo blocks client-scoped and third-party rules unconditionally and does not honor badfilter. It is not recommended as a full engine despite its small measurements. The naive trie is fast but allocates heavily: about 151 MiB per DNS process, about 301 MiB while both snapshots coexist. I am measuring a sorted compact index with the same parser/precedence logic before recommending an owned engine. The initial adblock adapter build failed because 0.13.5 changed Request::new and BlockerResult; source-verified corrections are queued. No candidate is selected. Perf receipt attached; full comparison follows after the remaining builds and gate.
Author
Owner

Crash-safety checkpoint pushed: branch research-dns-filtering, head 7c959a8f8 (WIP, not a finished gate claim). Source is under research/dns-filtering/; the isolated lock is included, production workspace untouched. First perf receipts and findings are in comment 72335. The next build ticket is PID 3697761 on compute01, queued by wrapper PID 3697759; log /home/claude/worktrees/r1-dns/research/dns-filtering/evidence/build-retry.log until gate.sh moves receipts to /home/claude/.cache/codex-scratch/r1-dns/. The one target is /home/claude/worktrees/r1-dns/target.

Next: finish compact-index/adblock/transport builds, run the staged perf-01 runner, finalize tables and owner questions, finish fast gate, replace WIP with a final research commit, and delete target. No perf process or fixture remains from the first completed measurement run.

Crash-safety checkpoint pushed: branch `research-dns-filtering`, head `7c959a8f8` (WIP, not a finished gate claim). Source is under `research/dns-filtering/`; the isolated lock is included, production workspace untouched. First perf receipts and findings are in comment 72335. The next build ticket is PID 3697761 on compute01, queued by wrapper PID 3697759; log `/home/claude/worktrees/r1-dns/research/dns-filtering/evidence/build-retry.log` until `gate.sh` moves receipts to `/home/claude/.cache/codex-scratch/r1-dns/`. The one target is `/home/claude/worktrees/r1-dns/target`. Next: finish compact-index/adblock/transport builds, run the staged perf-01 runner, finalize tables and owner questions, finish fast gate, replace WIP with a final research commit, and delete target. No perf process or fixture remains from the first completed measurement run.
Author
Owner

J48 progress (2026-11-01): DNS name selection now follows each receiver's compiled outbound policy, the same directional policy used for WireGuard. Added coverage for denied NXDOMAIN, one-way node and tag grants, narrow port grants, default allow, tag membership without grants, real hostnames, user endpoint rejection, and revoke removal in the next snapshot. The configured mesh DNS suffix is now carried with the accepted versioned snapshot; .internal remains the default.

The feature-on configuration test is queued in dev-02's build-slot. At the last check, the VM had 23 GB free, below its 25 GB admission threshold. I removed only J48's 1.2 GB target and restarted against the latest synced source; no Cargo process is running yet. Log path: /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/configuration-feature-on.log. The existing user identity model gap and owner question are recorded above and cross-referenced on #173.

J48 progress (2026-11-01): DNS name selection now follows each receiver's compiled outbound policy, the same directional policy used for WireGuard. Added coverage for denied NXDOMAIN, one-way node and tag grants, narrow port grants, default allow, tag membership without grants, real hostnames, user endpoint rejection, and revoke removal in the next snapshot. The configured mesh DNS suffix is now carried with the accepted versioned snapshot; `.internal` remains the default. The feature-on configuration test is queued in dev-02's `build-slot`. At the last check, the VM had 23 GB free, below its 25 GB admission threshold. I removed only J48's 1.2 GB target and restarted against the latest synced source; no Cargo process is running yet. Log path: `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/configuration-feature-on.log`. The existing user identity model gap and owner question are recorded above and cross-referenced on #173.
Author
Owner

Crash-safety snapshot pushed on mesh-dns at f5b5afd59 (WIP; will be squashed into the final passing change). It contains the outbound-policy DNS filter, staged name removals, suffix propagation, and policy/privacy regression coverage. The latest feature-on configuration test remains queued on dev-02 because free space is 23 GB, below the 25 GB build-slot threshold. No Cargo process is running; the captured log is /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/configuration-feature-on.log. Next: resume targeted tests as capacity permits, then compile-out, namespace and fast-gate verification.

Crash-safety snapshot pushed on `mesh-dns` at `f5b5afd59` (WIP; will be squashed into the final passing change). It contains the outbound-policy DNS filter, staged name removals, suffix propagation, and policy/privacy regression coverage. The latest feature-on configuration test remains queued on dev-02 because free space is 23 GB, below the 25 GB build-slot threshold. No Cargo process is running; the captured log is `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/configuration-feature-on.log`. Next: resume targeted tests as capacity permits, then compile-out, namespace and fast-gate verification.
Author
Owner

J48 policy audit after kayg's 2026-11-01 clarification:

The DNS filter uses each receiving node's compiled PeerPolicy.outbound, the same directed policy used to configure mesh reachability. The coordinator expands node and tag endpoints into directed node pairs; both_ways adds reverse access. Coverage now checks denied peers return NXDOMAIN and have no snapshot name, one-way node and tag grants publish only in the allowed direction, port-limited and default-allow policies publish reachable names, tag membership alone does not reveal names, and node/tag grant removal removes the name from the next staged snapshot. The existing privacy test remains about the mesh-view API projection; DNS intentionally serves the real coordinator hostname only to a receiver whose outbound policy allows that node. No inventory or mesh-view API behavior changed.

Policy-model gap: stage 1 has node and node-tag endpoints only. Endpoint::User is rejected by Arrow::validate and the Mesh API; Node has no user owner/device relation, and tags contain nodes rather than user identities/groups. D164 defers user arrows until stage 2. Thus this slice can enforce machine-to-machine and tag/group-of-nodes access, including directionality, but cannot express user-to-machine access or a user's devices/groups. No user policy was invented.

Owner question: to satisfy the clarified user-level lookup rule, what approved identity model should bind a querying user to mesh nodes and user/group selectors to nodes? Should user/device ownership and user/group grants be designed as a stage-2 follow-up, with this slice enforcing the current node/tag policy until that model exists? Cross-referenced to #173 because the gap is in grant expressiveness, not DNS visibility policy.

J48 policy audit after kayg's 2026-11-01 clarification: The DNS filter uses each receiving node's compiled `PeerPolicy.outbound`, the same directed policy used to configure mesh reachability. The coordinator expands node and tag endpoints into directed node pairs; `both_ways` adds reverse access. Coverage now checks denied peers return NXDOMAIN and have no snapshot name, one-way node and tag grants publish only in the allowed direction, port-limited and default-allow policies publish reachable names, tag membership alone does not reveal names, and node/tag grant removal removes the name from the next staged snapshot. The existing privacy test remains about the mesh-view API projection; DNS intentionally serves the real coordinator hostname only to a receiver whose outbound policy allows that node. No inventory or mesh-view API behavior changed. Policy-model gap: stage 1 has node and node-tag endpoints only. `Endpoint::User` is rejected by `Arrow::validate` and the Mesh API; `Node` has no user owner/device relation, and tags contain nodes rather than user identities/groups. D164 defers user arrows until stage 2. Thus this slice can enforce machine-to-machine and tag/group-of-nodes access, including directionality, but cannot express user-to-machine access or a user's devices/groups. No user policy was invented. Owner question: to satisfy the clarified user-level lookup rule, what approved identity model should bind a querying user to mesh nodes and user/group selectors to nodes? Should user/device ownership and user/group grants be designed as a stage-2 follow-up, with this slice enforcing the current node/tag policy until that model exists? Cross-referenced to #173 because the gap is in grant expressiveness, not DNS visibility policy.
Author
Owner

DNS filtering and forwarding options for #266

Recommendation for the owner's decision: keep slice 1 mesh-zone-only. For slice
2, prefer the measured compact index for a documented, strict DNS subset behind a replaceable interface,
provided unsupported syntax is visible and cannot silently turn exception-bearing
subscriptions into stronger blocking. For slice 3, prefer measuring/integrating
Hickory's transport/protocol stack through a zone-aware adapter over growing a
bespoke UDP relay into a full resolver. Its high-level Resolver API cannot be
connected blindly to the split-DNS server (see the measured CNAME probe below).
These are recommendations, not foundation choices or changes to D165/D197/D316.

simple-dns does not supply filter lists. It parses and encodes packets.
A subscription manager, a matching engine, and an upstream resolver are separate
pieces; none comes automatically with a small DNS wire library.

Existing mesh parser context

J7's earlier wire-only measurement
reported simple-dns / hickory-proto deltas of +105,664 / +344,864 bytes,
552 / 664 KiB warmed RSS at 1,000 peers, and 4.749 / 5.104 µs mean
parser+encoder time. Those were isolated packet harnesses, not filtering,
forwarding or end-to-end latency, and are not directly interchangeable with this
workload. Hickory-resolver adds substantially more than hickory-proto.

If the owner later chooses Hickory forwarding, its dependency graph already
includes hickory-proto. Measure whether moving the mesh-zone packet boundary to
that same implementation reduces the integrated size/maintenance cost instead
of carrying two DNS wire libraries indefinitely. That later integration delta
was not measured here; the first default-off mesh spike remains provisional.

Measured list engines

Built on compute01 through build-slot with Nix Rust/Cargo 1.99.0; measured on
perf-01 through perf-run (CPUs 1–3, 6 GiB scope). The final combined measurement
reported 35/6,055 jiffies steal (about 0.58%).
Three fresh processes per candidate, same 495,009 non-comment input rule lines
before hosts expansion/deduplication, 12,145,147 combined input bytes. Inputs:
AdGuard DNS filter 186,236 rules, OISD big 232,023, StevenBlack hosts 76,750.
lists.json pins source URLs, retrieval times and SHA-256. Standalone baseline:
392,632 bytes, static musl; all candidate executables have no PT_INTERP.

Numbers below are medians of three runs. RSS is KiB from /proc (shown as MiB),
not virtual memory. Warm RSS includes the query sample and approximately 1.53 MiB
of timing storage common to all matching engines. Source text is released before
matching and again after the second snapshot is built. The perf VM exposes an
AMD Ryzen 7 3700X and Linux 6.12.113; before/after receipts reported no process
over 5% CPU. These guest observations are not a claim of isolated physical cores.

Engine / representation Static bytes Delta vs baseline Warm RSS Compile Lookup p50 / p99 Full reload build Old + new resident snapshots
Own reversed-label BTreeMap trie 454,776 +62,144 152.43 MiB 509.4 ms 0.401 / 2.184 µs 533.2 ms 300.75 MiB
Own sorted compact domain index 456,056 +63,424 31.26 MiB 174.4 ms 0.721 / 4.038 µs 179.7 ms 55.84 MiB
adblock 0.13.5, DNS adapter 2,479,832 +2,087,200 53.76 MiB 483.4 ms 2.104 / 9.337 µs 444.3 ms 72.94 MiB
utilvo-shield-filters 1.0.0 453,528 +60,896 26.79 MiB 107.6 ms 0.281 / 0.741 µs 98.2 ms 49.46 MiB

These are isolated executable deltas, not integrated product deltas. The compact
index trades about 1.9 µs at p99 for about 121 MiB less RSS than the straightforward
trie. Its lookup p99 ranged 3.477–4.278 µs across runs; adblock ranged
9.197–9.928 µs. The sample timer and guest scheduling affect tails.

The owned variants accepted 494,123 expanded entries and rejected 895 entries;
there were 435,154 distinct normalized (domain, mode, exception, importance)
entries in the separate source audit. They produced the same 65,328 blocked
lookups per 100,000 sampled operations. adblock's parser accepted 495,002
adapter-produced lines, rejected seven as unrecognized options, and blocked
65,306 operations. utilvo stored 358,779 unique domains and blocked 63,656.
Those hit counts are not false-positive/negative rates or equivalent semantics.
The smaller utilvo set omits hosts/plain domains and exceptions.

Memory changes after warmup matter: adblock was only 23.34 MiB immediately after
compilation, then grew to 53.76 MiB when lookups materialized regex state. Reload
high-water RSS was about 312.34 MiB for the trie, 70.29 MiB compact, 134.46 MiB
adblock and 63.06 MiB utilvo. The old/new snapshot column retains the warmed old
snapshot and a newly compiled, not yet warmed replacement; simultaneous warm
traffic to both can cost more, especially for lazy regex state. Atomic pointer
replacement itself was not benchmarked, and these rebuild timings exclude
network download. A production implementation must account for readers still
holding the previous snapshot and bound its retirement.

Adblock serialization produced a 21,638,437-byte snapshot in median 6.04 ms;
deserialization took 5.93 ms. This can reduce restart cost, but does not eliminate
recompilation on a changed list, and serialized data needs bounded parsing and
version/integrity checks. The source exposes rebuild-and-replace, not arbitrary
in-place incremental edits.

Use one list snapshot per DNS process, shared by clients/queries. Fifty terminals
on one node do not require fifty lists. If fifty nodes each enable their own
filtering process, the measured warm totals are about 1.53 GiB for compact,
2.63 GiB for adblock, or 7.44 GiB for this naive trie across the fleet; whether
filtering is distributed to every node or supplied by explicit upstream services
is a separate deployment choice.

Syntax coverage observed

Rule / action Owned subset (both indexes) adblock DNS adapter utilvo
` domain^`, subdomains Works
@@ normal exceptions Works Works Dropped
$important block overrides normal exception Works Works Modifier ignored
Important exception overrides important block Works Fails DNS precedence probe Dropped
Hosts blocking entries / plain exact domains Works Works through adapter Not loaded
*.domain subdomains, excluding apex Works Works in probes Not supported
Wildcard inside hostname Rejected Works in probe Stored literally, ineffective
Hostname regex anchored at end Rejected Browser regex exists; synthetic URL fails hostname-regex probe Not supported
$badfilter rule disabling Rejected; remaining rule stays active Works in probe Ignored; rule stays active
$dnstype, $client, $dnsrewrite, $ctag, response-IP policy Rejected DNS semantics not implemented Unknown options can be discarded and overblock
Cosmetic/scriptlet/browser content rules Excluded from this DNS adapter Excluded from this DNS adapter Not a DNS action

The concrete important-exception probe was ||ads.example^$important together
with @@||ads.example^$important: the DNS specification expects allow; adblock
returned block. /ads\.example$/ matched against http://ads.example/ did not
match the hostname boundary. The adapter uses Request::preparsed and disables
embedded-domain-resolver, avoiding irrelevant public-suffix parsing while still
paying for synthetic-URL/token preparation. These mismatches are evidence against
calling the browser engine a drop-in DNS engine; no upstream patch is implied.

Full raw measurements and machine-state receipts are attached to #266. The first
trie/utilvo run preceded the compact-index addition; the table uses the
final frozen-source four-engine run; all probes and transports were repeated with
those final binaries after the repository gate. See README for the deterministic query distribution.

Licence findings

Candidate Pinned version Licence AGPL-3.0-only compatibility
Own subset prototype this branch AGPL-3.0-only Same licence as core
adblock 0.13.5 MPL-2.0 Yes via MPL §3.3 secondary-licence route, subject to the conditions below
utilvo-shield-filters 1.0.0 MIT Yes; keep copyright/permission notices
simple-dns 0.13.0 MIT Yes
hickory-resolver 0.26.3 MIT OR Apache-2.0 Yes; can elect MIT
dgaard-engine (source reviewed, not timed) 0.2.0 Apache-2.0 Yes; not a syntax-equivalent replacement
blocklist (source reviewed, not timed) 1.0.0 MIT Yes; embedded categories, not arbitrary subscription reloads

MPL is not automatically incompatible with AGPL, nor is every MPL package
unconditionally compatible. MPL 2.0 §1.12 explicitly names GNU AGPL version 3 as
a Secondary License. §3.3 permits the combination with an existing secondary-
licensed work when the covered software is not "Incompatible With Secondary
Licenses" under §1.5. The adblock 0.13.5 archive's source uses ordinary MPL 2.0
notices; no attached Exhibit B exclusion was found outside the complete licence
text. Mozilla FAQ Q25 expressly says that merely shipping the complete licence,
including its Exhibit B template, does not attach that exclusion. Preserve MPL
notices, provide the covered source under MPL, and additionally distribute it
under the secondary-licence terms applicable to the combined AGPL work. Do not
rewrite third-party headers to AGPL-only.

Sources: MPL text,
Mozilla compatibility FAQ, especially Q14 and Q25,
adblock source archive.
The isolated Cargo.lock records exact transitive checksums. The all-features isolated metadata contained 177 packages. Licence expressions
had AGPL-compatible selections (MIT/Apache where offered; ISC, BSD-3-Clause,
Unicode-3.0 and CDLA-Permissive-2.0 notices retained), with adblock the sole MPL
package requiring the secondary-licence analysis above. No dependency lacked a
licence declaration. This is a source/metadata audit, not a shipping cargo-deny
allowlist change. The product dependency
graph is unchanged; a future integration still needs its own cargo-deny review.
List content licences are independent of engine licences. The repositories for
AdGuard DNS filter
and OISD carry GPL v3;
StevenBlack/hosts
carries MIT, with source-list attribution also retained by that project. GPL v3
has an explicit AGPL v3 combination provision (§13), but an aggregated list's
repository licence should not replace checking its upstream attribution terms.
Lists were downloaded for this benchmark and are not redistributed in this branch.

Why browser rule matching is not DNS filtering

A DNS question gives a hostname, query type, and client identity. It does not give
a URL path, page origin, script/image type, or browser third-party context.
adblock can match network rules against synthetic URLs, but cannot infer those
missing facts. Passing a browser-oriented list wholesale can create both false
positives and false negatives. $dnstype, $dnsrewrite, $client, $ctag, and
response-address conditions need DNS-aware policy/action handling. A boolean
browser match cannot perform answer rewriting.

The prototype deliberately rejects unsupported rules; rejection does not imply
that the remaining subscription is equivalent. In particular, ignoring a
$badfilter disable or an unsupported allow rule can leave an active block in
place. A production loader should expose counts, source/rule explanations and
unsupported examples, and reject an unsupported subscription by default unless
the owner explicitly accepts partial semantics.

utilvo-shield-filters is maintained recently (unarchived, repository pushed
2026-10-20), but has a single Rust release and a very small implementation. It
extracts domains from ||...^, discards exceptions, and ignores unknown option
semantics. Its low cost must be read with its smaller effective loaded rule set.
It is not a drop-in option for the requested subset. dgaard-engine is maintained
(unarchived, pushed 2026-10-29) and DNS-native, but combines list matching with
entropy, n-gram, ASN, GeoIP, and suspicion scoring. That is a broader product
policy than the requested syntax subset. blocklist compiles fixed categories
into FSTs at build time; runtime arbitrary list subscriptions/reloads are not its
API. Neither was smuggled in as a foundation selection.

Measured upstream forwarding

Same pinned perf machine and loopback fixture, three fresh processes per
transport, 1,000 unique names followed by 1,000 repeats. All ordinary responses
were accepted successfully. Certificate and hostname verification stayed enabled.
The encrypted Hickory build includes UDP/TCP, DoT, HTTP/2 DoH, public trust roots,
and runtime-selectable DNSSEC validation; the plain build includes UDP/TCP/cache.

Standalone build Bytes Delta vs 392,632-byte floor
Own UDP on simple-dns + Tokio 709,896 +317,264
Own UDP + Hyper HTTP/1 DoH + Rustls/ring 2,275,984 +1,883,352
Hickory UDP/TCP/cache 1,686,760 +1,294,128
Hickory UDP/TCP/DoT/DoH/cache/DNSSEC 3,798,032 +3,405,400

Each is static musl without PT_INTERP. Do not sum alternative rows or treat them
as additions to the shipping binary: its existing runtime, Hyper, Rustls/ring and
OpenSSL mean integrated deltas require a later integration measurement.

Transport Miss p50 / p99 Repeat p50 / p99 RSS after miss + repeat phases
Own UDP, no cache 36.318 / 111.019 µs 35.196 / 131.657 µs 0.71 MiB
Own HTTP/1 DoH, no cache 50.074 / 200.927 µs 53.821 / 219.712 µs 2.25 MiB
Hickory UDP 87.925 / 232.515 µs 1.152 / 3.306 µs 2.82 MiB
Hickory TCP 67.376 / 229.050 µs 1.143 / 3.547 µs 2.71 MiB
Hickory DoT 74.840 / 363.552 µs 1.172 / 3.386 µs 4.61 MiB
Hickory HTTP/2 DoH 93.816 / 229.429 µs 1.182 / 3.477 µs 4.65 MiB

These are medians of run quantiles, not pooled distributions. DoT miss p99 ranged
183.243–457.437 µs, showing tail variability even in this fixture. Own HTTP/1 DoH repeat p99
ranged 97.541–1,169.952 µs; that outlying run remains in the raw receipt and
is not hidden by the median. The own path
wins uncached loopback latency but does less work; Hickory's cache removes the
upstream round trip on repeat queries. The fixture's HTTP adapters differ, so do
not attribute all HTTP/1-vs-HTTP/2 differences to client implementation cost.
No WAN/netem, throughput, overload, negative-cache expiry, DNS cache poisoning,
or concurrent-client correctness claim follows from this sequential fixture.

With 8,192 unique cached names, one additional Hickory UDP scale run reached
10,684 KiB RSS (10.43 MiB); cache-hit p50/p99 were 1.413/3.997 µs. This is one
scale run, not a three-run median. At fifty such DNS processes the measured cache
RSS alone would total about 522 MiB, before filtering; configure cache size as a
shared per-process budget, not per-client storage.

The TC-bit probe answered 1,000 UDP questions with truncation, then repeated a
cached name 1,000 times. The own UDP path could not resolve any of the 2,000
operations because it has no TCP fallback. Hickory resolved all 2,000, performing
TCP fallback then using its cache. This is a concrete correctness cost omitted
from the smaller own-forwarder size. The owned path's sequential ids, retry and
admission behavior are harness simplifications, not production security design.

Own DoH connects before timing the query loop: setup/handshake ranged
1.42–2.02 ms. Hickory connects lazily: first DoT miss was 1.57–1.95 ms, first DoH
miss 1.54–1.94 ms plus 0.07–0.09 ms setup. The steady-state quantiles must not be
read as cold connection costs. No TLS-verification bypass was used.

A separate explicit public DoH smoke run enabled local validation using
1.1.1.1 with server name cloudflare-dns.com. cloudflare.com A and RRSIG records
were classified proof: Secure; dnssec-failed.org returned SERVFAIL. The public
upstream also validates, so the bogus-name refusal does not isolate local
rejection from upstream rejection. These probes demonstrate an enabled validation
path and a secure positive answer; they do not replace a controlled DNSSEC corpus
for bogus signatures, insecure delegations, negative proofs, expiry and rollover.
Their WAN timings are deliberately excluded from the loopback table.

Upstream correctness and DNSSEC

Dispatch owned mesh suffixes before external filtering/forwarding. Unknown or
policy-invisible mesh names stay authoritative local negatives and never leak to
an upstream. External filtering applies only when that DNS service is explicitly
enabled; it does not silently replace the host's split-DNS configuration. A first
QNAME-only filter also needs to say whether returned CNAME targets are inspected;
CNAME-cloaking protection is additional response policy, not implied by matching
the original question. DNS filtering cannot distinguish ads from useful content
served under the same hostname, and does not control clients using their own DNS
transport outside the configured service.

A traced fixture returned alias0.bench.test CNAME hidden.internal. Calling
Hickory's high-level Resolver::lookup(A) sent both names to the configured
external upstream and returned CNAME+A. This is ordinary stub-resolver behavior,
not a library vulnerability, but initial-QNAME-only dispatch is insufficient for
D165 and reachability-gated mesh names: every internally chased name needs local
zone policy. External answer/alias data must not populate or override authoritative
mesh records, and mesh cache hits must still enforce the requesting client's
current reachability policy. The high-level Lookup is also not a transparent DNS wire response:
headers, authority/negative responses, EDNS and AD/CD/DO need an explicit server
contract. Prefer a zone-aware adapter over the transport/protocol layer, or
packet-level forwarding that returns the alias for normal split-DNS resolution;
measure that actual integration before choosing its footprint. Cache timings here
are a high-level resolver baseline, not evidence that it is safe to wire into the
mesh service unchanged. Reproduce with cname-probe.sh; the query trace is attached
to the finding.

Hickory offers UDP/TCP, DoT and HTTP/2 DoH behind explicit features, bounded
caching with TTLs, negative caching, CNAME handling, retries/timeouts, and TCP
fallback. Server ordering defaults to query statistics, and concurrent requests
default to two: an integration preserving the owner's configured upstream order
must explicitly use UserProvidedOrder and one concurrent upstream request. No
public resolver is silently selected in these harnesses. DoQ/HTTP3 are further
feature choices, not required for encrypted upstreams in slice 3.

The own path reuses Hyper HTTP/1, which the product already ships, and Rustls/ring,
which it already ships for mesh transport. Existing product HTTP callers use
OpenSSL; this harness measures the alternative using the already present Rustls
provider, not the incremental cost of changing those callers. RFC 8484 permits
HTTP/1.1 though HTTP/2 is recommended. Connection reuse matters more than the
small parser cost; reconnect/handshake and adverse-network measurements are
still needed before choosing the production transport. HTTPS certificate/hostname
validation and bootstrap IPs must remain explicit, including loop avoidance when
the resolver itself needs DNS to find an upstream hostname.

DNSSEC is distinct from encrypted transport. Hickory's dnssec-ring feature
supplies local validation; options.validate is false by default and must be
explicitly enabled with trust anchors. The encrypted benchmark links that path
but times unsigned fixture answers with validation off. The separate public smoke probe above classified a valid signed answer Secure;
it does not qualify controlled bogus, expired-signature, NSEC/NSEC3 or
trust-anchor-rollover behavior. The own
simple-dns forwarder performs no DNSSEC validation. A byte-preserving
forwarder could carry EDNS/DO and signed packets, but this A-query harness does
not qualify their preservation and cannot set AD based on local validation.
A production filtering resolver must clear AD on locally synthesized/modified
answers and define its CD/DO/AD policy. Trusting an upstream's AD bit is a trust
choice, not equivalent to validating locally.

Source: Hickory 0.26.3 configuration and feature docs,
RFC 8484,
DNSSEC validation requirements, RFC 4035.

What the broader DNS product adds

Capability What matters here Suggested stage
Blocklist subscriptions HTTPS fetch, timeouts/body limits, conditional requests, validated format, dedup, attribution, scheduled refresh, last-good rollback, atomic snapshot swap Slice 2 minimum
Exceptions and per-client policy One global list snapshot with small overrides keyed by authenticated mesh identity; explanation of the winning rule; do not duplicate a full list per client Global exceptions in slice 2; per-client policy after its own grill
Safe search Provider-specific DNS rewrites and maintenance of supported endpoints; not a generic blocklist toggle, and DNS cannot force every app/browser to comply Later opt-in policy
Query log Useful for "why was this blocked"; names reveal activity, so bounded retention, access control, redaction/export rules and an off switch are required product decisions Bounded counters/recent diagnostic ring first; persistent history later
DoH/DoT/DoQ server TLS listener/certificate lifecycle, authorization, rate/concurrency limits and exposure policy; independent of using encrypted upstreams Defer server transports; mesh-only DNS already rides encrypted WireGuard
DNS rewrites, parental/malware services, DHCP Additional policy/data lifecycles; not supplied by the wire parser or list matcher Outside the first filtering slice

Sources: AdGuard Home feature comparison,
configuration and API,
DNS filter syntax,
DNS proxy transport support.

Owner grill, recommendation first

  1. Recommend mesh-zone-only for slice 1. Keep the small parser behind the
    existing default-off spike until chosen. Publish real hostnames only to peers
    allowed to reach them, as the 2026-11-01 owner comment requires. Preserve
    split DNS and OS upstreams (D165), authoritative NXDOMAIN for unknown owned
    names, accepted-snapshot replacement and coordinator-outage behavior. Accept
    this narrow first slice before adding filtering/forwarding?
  2. Recommend a strict DNS subset for slice 2, not a browser-engine promise of
    full compatibility.
    Is a clearly documented subset with visible unsupported
    rule diagnostics acceptable, or does the first filtering release need full
    AdGuard DNS rule/action compatibility? The latter is a materially larger
    engine/policy contract. Start with OISD/domain/hosts subscriptions plus explicit
    exceptions, or require the additional patterns and $badfilter found in the
    current AdGuard DNS filter before accepting that subscription? The prototype
    does not decide that trade-off. The compact representation is the recommended
    owned candidate; the naive trie spends too much memory for its small p99 win.
    Blocked-answer behavior (NXDOMAIN, NODATA or null addresses), negative TTL and
    response-CNAME filtering still need their own correctness contract before
    implementing the filtering service.
  3. Recommend a single shared list snapshot per DNS process. Should filtering
    apply only to an explicit mesh DNS service, with system-wide adoption opt-in,
    and global allow/deny overrides first? Per-client policies should reuse the
    authenticated mesh identity and the same snapshot; untrusted source IP alone
    must not grant a policy exception. Should reload failures retain last-good
    rules and continue serving, with a visible refresh error?
  4. Recommend measuring a zone-aware adapter over Hickory transports for slice
    3 if the measured footprint is acceptable.
    The high-level Resolver follows
    CNAMEs into the configured upstream, so initial-QNAME dispatch alone violates
    the mesh privacy boundary. Require local zone policy on every chase and an
    explicit wire-response contract before integration; its current cache/latency
    numbers are baselines. This reuses TCP fallback and encrypted transports while
    keeping mesh routing explicit. Prefer owner-set
    authenticated DoH/DoT upstreams, ordered explicitly, no automatic downgrade to
    plaintext. Is local DNSSEC validation required in that slice, or a separately
    grilled follow-up? Do not equate DoH with DNSSEC.
  5. Recommend deferring public DoH/DoT/DoQ serving and persistent query history.
    Does a bounded diagnostic view plus counters satisfy the first troubleshooting
    need? Persistent history and public listener exposure need separate privacy,
    retention and access decisions; neither is necessary to resolve mesh names.

Verification

The full scripts/verify fast run on compute01 exited 1: all twelve Rust gate
rows passed, including static x86 and aarch64 builds, but one existing frontend
layout timing assertion failed (363 pass, 1 fail in that tracer batch).
layout.test.ts:75 expected average <25 ms and measured 40.465742 ms. The shipping
static sizes exactly matched their existing ceilings: 21,443,824 and 19,629,240
bytes; PT_INTERP absent. No production source or dependency changed in this job.
An isolated test from an unmodified base snapshot passed (8 pass, 0 fail). The
complete TypeScript gate then also passed on a git archive origin/dev snapshot
of the base commit using the same installed dependencies (exit 0), including
364/364 tests in the previously failing batch. The timing failure did not
reproduce; its precise cause is unproven. This does not turn the original full
verify fast exit into a pass. No timing threshold was changed. The failed full gate and baseline receipts are
attached to the gate finding.

Final receipts: combined measurements and correctness probes,
perf machine before/after,
frozen-source static build,
full fast gate,
full baseline TypeScript gate.
The isolated harness formatting check passed. The build target directory was
deleted after preserving binaries and receipts; the measurement runners removed
their fixture processes and temporary certificate material before releasing the
perf lock. No foundation was selected and no production dependency changed.

Final harness/report commit: 11bbef334cd53d62b08c8ab297b1beec311ecfa8, branch research-dns-filtering. Research only; owner choices remain open.

# DNS filtering and forwarding options for #266 Recommendation for the owner's decision: keep slice 1 mesh-zone-only. For slice 2, prefer the measured compact index for a documented, strict DNS subset behind a replaceable interface, provided unsupported syntax is visible and cannot silently turn exception-bearing subscriptions into stronger blocking. For slice 3, prefer measuring/integrating Hickory's transport/protocol stack through a zone-aware adapter over growing a bespoke UDP relay into a full resolver. Its high-level Resolver API cannot be connected blindly to the split-DNS server (see the measured CNAME probe below). These are recommendations, not foundation choices or changes to D165/D197/D316. `simple-dns` does **not** supply filter lists. It parses and encodes packets. A subscription manager, a matching engine, and an upstream resolver are separate pieces; none comes automatically with a small DNS wire library. ## Existing mesh parser context [J7's earlier wire-only measurement](https://git.kayg.org/kayg/infraternal/issues/266#issuecomment-71293) reported simple-dns / hickory-proto deltas of +105,664 / +344,864 bytes, 552 / 664 KiB warmed RSS at 1,000 peers, and 4.749 / 5.104 µs mean parser+encoder time. Those were isolated packet harnesses, not filtering, forwarding or end-to-end latency, and are not directly interchangeable with this workload. Hickory-resolver adds substantially more than hickory-proto. If the owner later chooses Hickory forwarding, its dependency graph already includes hickory-proto. Measure whether moving the mesh-zone packet boundary to that same implementation reduces the integrated size/maintenance cost instead of carrying two DNS wire libraries indefinitely. That later integration delta was not measured here; the first default-off mesh spike remains provisional. ## Measured list engines Built on compute01 through build-slot with Nix Rust/Cargo 1.99.0; measured on perf-01 through perf-run (CPUs 1–3, 6 GiB scope). The final combined measurement reported 35/6,055 jiffies steal (about 0.58%). Three fresh processes per candidate, same 495,009 non-comment input rule lines before hosts expansion/deduplication, 12,145,147 combined input bytes. Inputs: AdGuard DNS filter 186,236 rules, OISD big 232,023, StevenBlack hosts 76,750. `lists.json` pins source URLs, retrieval times and SHA-256. Standalone baseline: 392,632 bytes, static musl; all candidate executables have no PT_INTERP. Numbers below are medians of three runs. RSS is KiB from /proc (shown as MiB), not virtual memory. Warm RSS includes the query sample and approximately 1.53 MiB of timing storage common to all matching engines. Source text is released before matching and again after the second snapshot is built. The perf VM exposes an AMD Ryzen 7 3700X and Linux 6.12.113; before/after receipts reported no process over 5% CPU. These guest observations are not a claim of isolated physical cores. | Engine / representation | Static bytes | Delta vs baseline | Warm RSS | Compile | Lookup p50 / p99 | Full reload build | Old + new resident snapshots | |---|---:|---:|---:|---:|---:|---:|---:| | Own reversed-label BTreeMap trie | 454,776 | +62,144 | 152.43 MiB | 509.4 ms | 0.401 / 2.184 µs | 533.2 ms | 300.75 MiB | | Own sorted compact domain index | 456,056 | +63,424 | 31.26 MiB | 174.4 ms | 0.721 / 4.038 µs | 179.7 ms | 55.84 MiB | | `adblock` 0.13.5, DNS adapter | 2,479,832 | +2,087,200 | 53.76 MiB | 483.4 ms | 2.104 / 9.337 µs | 444.3 ms | 72.94 MiB | | `utilvo-shield-filters` 1.0.0 | 453,528 | +60,896 | 26.79 MiB | 107.6 ms | 0.281 / 0.741 µs | 98.2 ms | 49.46 MiB | These are isolated executable deltas, not integrated product deltas. The compact index trades about 1.9 µs at p99 for about 121 MiB less RSS than the straightforward trie. Its lookup p99 ranged 3.477–4.278 µs across runs; adblock ranged 9.197–9.928 µs. The sample timer and guest scheduling affect tails. The owned variants accepted 494,123 expanded entries and rejected 895 entries; there were 435,154 distinct normalized (domain, mode, exception, importance) entries in the separate source audit. They produced the same 65,328 blocked lookups per 100,000 sampled operations. `adblock`'s parser accepted 495,002 adapter-produced lines, rejected seven as unrecognized options, and blocked 65,306 operations. `utilvo` stored 358,779 unique domains and blocked 63,656. Those hit counts **are not false-positive/negative rates or equivalent semantics**. The smaller utilvo set omits hosts/plain domains and exceptions. Memory changes after warmup matter: adblock was only 23.34 MiB immediately after compilation, then grew to 53.76 MiB when lookups materialized regex state. Reload high-water RSS was about 312.34 MiB for the trie, 70.29 MiB compact, 134.46 MiB adblock and 63.06 MiB utilvo. The old/new snapshot column retains the warmed old snapshot and a newly compiled, not yet warmed replacement; simultaneous warm traffic to both can cost more, especially for lazy regex state. Atomic pointer replacement itself was not benchmarked, and these rebuild timings exclude network download. A production implementation must account for readers still holding the previous snapshot and bound its retirement. Adblock serialization produced a 21,638,437-byte snapshot in median 6.04 ms; deserialization took 5.93 ms. This can reduce restart cost, but does not eliminate recompilation on a changed list, and serialized data needs bounded parsing and version/integrity checks. The source exposes rebuild-and-replace, not arbitrary in-place incremental edits. Use one list snapshot per DNS process, shared by clients/queries. Fifty terminals on one node do not require fifty lists. If fifty nodes each enable their own filtering process, the measured warm totals are about 1.53 GiB for compact, 2.63 GiB for adblock, or 7.44 GiB for this naive trie across the fleet; whether filtering is distributed to every node or supplied by explicit upstream services is a separate deployment choice. ### Syntax coverage observed | Rule / action | Owned subset (both indexes) | adblock DNS adapter | utilvo | |---|---|---|---| | `||domain^`, subdomains | Works | Works | Works | | `@@` normal exceptions | Works | Works | Dropped | | `$important` block overrides normal exception | Works | Works | Modifier ignored | | Important exception overrides important block | Works | **Fails DNS precedence probe** | Dropped | | Hosts blocking entries / plain exact domains | Works | Works through adapter | Not loaded | | `*.domain` subdomains, excluding apex | Works | Works in probes | Not supported | | Wildcard inside hostname | Rejected | Works in probe | Stored literally, ineffective | | Hostname regex anchored at end | Rejected | Browser regex exists; **synthetic URL fails hostname-regex probe** | Not supported | | `$badfilter` rule disabling | Rejected; remaining rule stays active | Works in probe | Ignored; rule stays active | | `$dnstype`, `$client`, `$dnsrewrite`, `$ctag`, response-IP policy | Rejected | DNS semantics not implemented | Unknown options can be discarded and overblock | | Cosmetic/scriptlet/browser content rules | Excluded from this DNS adapter | Excluded from this DNS adapter | Not a DNS action | The concrete important-exception probe was `||ads.example^$important` together with `@@||ads.example^$important`: the DNS specification expects allow; adblock returned block. `/ads\.example$/` matched against `http://ads.example/` did not match the hostname boundary. The adapter uses Request::preparsed and disables embedded-domain-resolver, avoiding irrelevant public-suffix parsing while still paying for synthetic-URL/token preparation. These mismatches are evidence against calling the browser engine a drop-in DNS engine; no upstream patch is implied. Full raw measurements and machine-state receipts are attached to #266. The first trie/utilvo run preceded the compact-index addition; the table uses the final frozen-source four-engine run; all probes and transports were repeated with those final binaries after the repository gate. See README for the deterministic query distribution. ## Licence findings | Candidate | Pinned version | Licence | AGPL-3.0-only compatibility | |---|---|---|---| | Own subset prototype | this branch | AGPL-3.0-only | Same licence as core | | `adblock` | 0.13.5 | MPL-2.0 | Yes via MPL §3.3 secondary-licence route, subject to the conditions below | | `utilvo-shield-filters` | 1.0.0 | MIT | Yes; keep copyright/permission notices | | `simple-dns` | 0.13.0 | MIT | Yes | | `hickory-resolver` | 0.26.3 | MIT OR Apache-2.0 | Yes; can elect MIT | | `dgaard-engine` (source reviewed, not timed) | 0.2.0 | Apache-2.0 | Yes; not a syntax-equivalent replacement | | `blocklist` (source reviewed, not timed) | 1.0.0 | MIT | Yes; embedded categories, not arbitrary subscription reloads | MPL is not automatically incompatible with AGPL, nor is every MPL package unconditionally compatible. MPL 2.0 §1.12 explicitly names GNU AGPL version 3 as a Secondary License. §3.3 permits the combination with an existing secondary- licensed work when the covered software is not "Incompatible With Secondary Licenses" under §1.5. The `adblock` 0.13.5 archive's source uses ordinary MPL 2.0 notices; no attached Exhibit B exclusion was found outside the complete licence text. Mozilla FAQ Q25 expressly says that merely shipping the complete licence, including its Exhibit B template, does not attach that exclusion. Preserve MPL notices, provide the covered source under MPL, and additionally distribute it under the secondary-licence terms applicable to the combined AGPL work. Do not rewrite third-party headers to AGPL-only. Sources: [MPL text](https://www.mozilla.org/en-US/MPL/2.0/), [Mozilla compatibility FAQ, especially Q14 and Q25](https://www.mozilla.org/en-US/MPL/2.0/FAQ/), [`adblock` source archive](https://crates.io/api/v1/crates/adblock/0.13.5/download). The isolated Cargo.lock records exact transitive checksums. The all-features isolated metadata contained 177 packages. Licence expressions had AGPL-compatible selections (MIT/Apache where offered; ISC, BSD-3-Clause, Unicode-3.0 and CDLA-Permissive-2.0 notices retained), with adblock the sole MPL package requiring the secondary-licence analysis above. No dependency lacked a licence declaration. This is a source/metadata audit, not a shipping cargo-deny allowlist change. The product dependency graph is unchanged; a future integration still needs its own cargo-deny review. List content licences are independent of engine licences. The repositories for [AdGuard DNS filter](https://github.com/AdguardTeam/AdGuardSDNSFilter/blob/master/LICENSE) and [OISD](https://github.com/sjhgvr/oisd/blob/main/LICENSE) carry GPL v3; [StevenBlack/hosts](https://github.com/StevenBlack/hosts/blob/master/license.txt) carries MIT, with source-list attribution also retained by that project. GPL v3 has an explicit AGPL v3 combination provision (§13), but an aggregated list's repository licence should not replace checking its upstream attribution terms. Lists were downloaded for this benchmark and are not redistributed in this branch. ## Why browser rule matching is not DNS filtering A DNS question gives a hostname, query type, and client identity. It does not give a URL path, page origin, script/image type, or browser third-party context. `adblock` can match network rules against synthetic URLs, but cannot infer those missing facts. Passing a browser-oriented list wholesale can create both false positives and false negatives. `$dnstype`, `$dnsrewrite`, `$client`, `$ctag`, and response-address conditions need DNS-aware policy/action handling. A boolean browser match cannot perform answer rewriting. The prototype deliberately rejects unsupported rules; rejection does not imply that the remaining subscription is equivalent. In particular, ignoring a `$badfilter` disable or an unsupported allow rule can leave an active block in place. A production loader should expose counts, source/rule explanations and unsupported examples, and reject an unsupported subscription by default unless the owner explicitly accepts partial semantics. `utilvo-shield-filters` is maintained recently (unarchived, repository pushed 2026-10-20), but has a single Rust release and a very small implementation. It extracts domains from `||...^`, discards exceptions, and ignores unknown option semantics. Its low cost must be read with its smaller effective loaded rule set. It is not a drop-in option for the requested subset. `dgaard-engine` is maintained (unarchived, pushed 2026-10-29) and DNS-native, but combines list matching with entropy, n-gram, ASN, GeoIP, and suspicion scoring. That is a broader product policy than the requested syntax subset. `blocklist` compiles fixed categories into FSTs at build time; runtime arbitrary list subscriptions/reloads are not its API. Neither was smuggled in as a foundation selection. ## Measured upstream forwarding Same pinned perf machine and loopback fixture, three fresh processes per transport, 1,000 unique names followed by 1,000 repeats. All ordinary responses were accepted successfully. Certificate and hostname verification stayed enabled. The encrypted Hickory build includes UDP/TCP, DoT, HTTP/2 DoH, public trust roots, and runtime-selectable DNSSEC validation; the plain build includes UDP/TCP/cache. | Standalone build | Bytes | Delta vs 392,632-byte floor | |---|---:|---:| | Own UDP on simple-dns + Tokio | 709,896 | +317,264 | | Own UDP + Hyper HTTP/1 DoH + Rustls/ring | 2,275,984 | +1,883,352 | | Hickory UDP/TCP/cache | 1,686,760 | +1,294,128 | | Hickory UDP/TCP/DoT/DoH/cache/DNSSEC | 3,798,032 | +3,405,400 | Each is static musl without PT_INTERP. Do not sum alternative rows or treat them as additions to the shipping binary: its existing runtime, Hyper, Rustls/ring and OpenSSL mean integrated deltas require a later integration measurement. | Transport | Miss p50 / p99 | Repeat p50 / p99 | RSS after miss + repeat phases | |---|---:|---:|---:| | Own UDP, no cache | 36.318 / 111.019 µs | 35.196 / 131.657 µs | 0.71 MiB | | Own HTTP/1 DoH, no cache | 50.074 / 200.927 µs | 53.821 / 219.712 µs | 2.25 MiB | | Hickory UDP | 87.925 / 232.515 µs | 1.152 / 3.306 µs | 2.82 MiB | | Hickory TCP | 67.376 / 229.050 µs | 1.143 / 3.547 µs | 2.71 MiB | | Hickory DoT | 74.840 / 363.552 µs | 1.172 / 3.386 µs | 4.61 MiB | | Hickory HTTP/2 DoH | 93.816 / 229.429 µs | 1.182 / 3.477 µs | 4.65 MiB | These are medians of run quantiles, not pooled distributions. DoT miss p99 ranged 183.243–457.437 µs, showing tail variability even in this fixture. Own HTTP/1 DoH repeat p99 ranged 97.541–1,169.952 µs; that outlying run remains in the raw receipt and is not hidden by the median. The own path wins uncached loopback latency but does less work; Hickory's cache removes the upstream round trip on repeat queries. The fixture's HTTP adapters differ, so do not attribute all HTTP/1-vs-HTTP/2 differences to client implementation cost. No WAN/netem, throughput, overload, negative-cache expiry, DNS cache poisoning, or concurrent-client correctness claim follows from this sequential fixture. With 8,192 unique cached names, one additional Hickory UDP scale run reached 10,684 KiB RSS (10.43 MiB); cache-hit p50/p99 were 1.413/3.997 µs. This is one scale run, not a three-run median. At fifty such DNS processes the measured cache RSS alone would total about 522 MiB, before filtering; configure cache size as a shared per-process budget, not per-client storage. The TC-bit probe answered 1,000 UDP questions with truncation, then repeated a cached name 1,000 times. The own UDP path could not resolve any of the 2,000 operations because it has no TCP fallback. Hickory resolved all 2,000, performing TCP fallback then using its cache. This is a concrete correctness cost omitted from the smaller own-forwarder size. The owned path's sequential ids, retry and admission behavior are harness simplifications, not production security design. Own DoH connects before timing the query loop: setup/handshake ranged 1.42–2.02 ms. Hickory connects lazily: first DoT miss was 1.57–1.95 ms, first DoH miss 1.54–1.94 ms plus 0.07–0.09 ms setup. The steady-state quantiles must not be read as cold connection costs. No TLS-verification bypass was used. A separate explicit public DoH smoke run enabled local validation using 1.1.1.1 with server name cloudflare-dns.com. `cloudflare.com` A and RRSIG records were classified `proof: Secure`; `dnssec-failed.org` returned SERVFAIL. The public upstream also validates, so the bogus-name refusal does not isolate local rejection from upstream rejection. These probes demonstrate an enabled validation path and a secure positive answer; they do not replace a controlled DNSSEC corpus for bogus signatures, insecure delegations, negative proofs, expiry and rollover. Their WAN timings are deliberately excluded from the loopback table. ## Upstream correctness and DNSSEC Dispatch owned mesh suffixes before external filtering/forwarding. Unknown or policy-invisible mesh names stay authoritative local negatives and never leak to an upstream. External filtering applies only when that DNS service is explicitly enabled; it does not silently replace the host's split-DNS configuration. A first QNAME-only filter also needs to say whether returned CNAME targets are inspected; CNAME-cloaking protection is additional response policy, not implied by matching the original question. DNS filtering cannot distinguish ads from useful content served under the same hostname, and does not control clients using their own DNS transport outside the configured service. A traced fixture returned `alias0.bench.test CNAME hidden.internal`. Calling Hickory's high-level `Resolver::lookup(A)` sent **both** names to the configured external upstream and returned CNAME+A. This is ordinary stub-resolver behavior, not a library vulnerability, but initial-QNAME-only dispatch is insufficient for D165 and reachability-gated mesh names: every internally chased name needs local zone policy. External answer/alias data must not populate or override authoritative mesh records, and mesh cache hits must still enforce the requesting client's current reachability policy. The high-level `Lookup` is also not a transparent DNS wire response: headers, authority/negative responses, EDNS and AD/CD/DO need an explicit server contract. Prefer a zone-aware adapter over the transport/protocol layer, or packet-level forwarding that returns the alias for normal split-DNS resolution; measure that actual integration before choosing its footprint. Cache timings here are a high-level resolver baseline, not evidence that it is safe to wire into the mesh service unchanged. Reproduce with `cname-probe.sh`; the query trace is attached to [the finding](https://git.kayg.org/kayg/infraternal/issues/266#issuecomment-72382). Hickory offers UDP/TCP, DoT and HTTP/2 DoH behind explicit features, bounded caching with TTLs, negative caching, CNAME handling, retries/timeouts, and TCP fallback. Server ordering defaults to query statistics, and concurrent requests default to two: an integration preserving the owner's configured upstream order must explicitly use UserProvidedOrder and one concurrent upstream request. No public resolver is silently selected in these harnesses. DoQ/HTTP3 are further feature choices, not required for encrypted upstreams in slice 3. The own path reuses Hyper HTTP/1, which the product already ships, and Rustls/ring, which it already ships for mesh transport. Existing product HTTP callers use OpenSSL; this harness measures the alternative using the already present Rustls provider, not the incremental cost of changing those callers. RFC 8484 permits HTTP/1.1 though HTTP/2 is recommended. Connection reuse matters more than the small parser cost; reconnect/handshake and adverse-network measurements are still needed before choosing the production transport. HTTPS certificate/hostname validation and bootstrap IPs must remain explicit, including loop avoidance when the resolver itself needs DNS to find an upstream hostname. DNSSEC is distinct from encrypted transport. Hickory's `dnssec-ring` feature supplies local validation; options.validate is false by default and must be explicitly enabled with trust anchors. The encrypted benchmark links that path but times unsigned fixture answers with validation off. The separate public smoke probe above classified a valid signed answer Secure; it does not qualify controlled bogus, expired-signature, NSEC/NSEC3 or trust-anchor-rollover behavior. The own simple-dns forwarder performs no DNSSEC validation. A byte-preserving forwarder could carry EDNS/DO and signed packets, but this A-query harness does not qualify their preservation and cannot set AD based on local validation. A production filtering resolver must clear AD on locally synthesized/modified answers and define its CD/DO/AD policy. Trusting an upstream's AD bit is a trust choice, not equivalent to validating locally. Source: [Hickory 0.26.3 configuration and feature docs](https://docs.rs/hickory-resolver/0.26.3/hickory_resolver/), [RFC 8484](https://www.rfc-editor.org/rfc/rfc8484), [DNSSEC validation requirements, RFC 4035](https://www.rfc-editor.org/rfc/rfc4035). ## What the broader DNS product adds | Capability | What matters here | Suggested stage | |---|---|---| | Blocklist subscriptions | HTTPS fetch, timeouts/body limits, conditional requests, validated format, dedup, attribution, scheduled refresh, last-good rollback, atomic snapshot swap | Slice 2 minimum | | Exceptions and per-client policy | One global list snapshot with small overrides keyed by authenticated mesh identity; explanation of the winning rule; do not duplicate a full list per client | Global exceptions in slice 2; per-client policy after its own grill | | Safe search | Provider-specific DNS rewrites and maintenance of supported endpoints; not a generic blocklist toggle, and DNS cannot force every app/browser to comply | Later opt-in policy | | Query log | Useful for "why was this blocked"; names reveal activity, so bounded retention, access control, redaction/export rules and an off switch are required product decisions | Bounded counters/recent diagnostic ring first; persistent history later | | DoH/DoT/DoQ server | TLS listener/certificate lifecycle, authorization, rate/concurrency limits and exposure policy; independent of using encrypted upstreams | Defer server transports; mesh-only DNS already rides encrypted WireGuard | | DNS rewrites, parental/malware services, DHCP | Additional policy/data lifecycles; not supplied by the wire parser or list matcher | Outside the first filtering slice | Sources: [AdGuard Home feature comparison](https://github.com/AdguardTeam/AdGuardHome#comparison), [configuration and API](https://github.com/AdguardTeam/AdGuardHome/blob/master/openapi/openapi.yaml), [DNS filter syntax](https://adguard-dns.io/kb/general/dns-filtering-syntax/), [DNS proxy transport support](https://github.com/AdguardTeam/dnsproxy). ## Owner grill, recommendation first 1. **Recommend mesh-zone-only for slice 1.** Keep the small parser behind the existing default-off spike until chosen. Publish real hostnames only to peers allowed to reach them, as the 2026-11-01 owner comment requires. Preserve split DNS and OS upstreams (D165), authoritative NXDOMAIN for unknown owned names, accepted-snapshot replacement and coordinator-outage behavior. Accept this narrow first slice before adding filtering/forwarding? 2. **Recommend a strict DNS subset for slice 2, not a browser-engine promise of full compatibility.** Is a clearly documented subset with visible unsupported rule diagnostics acceptable, or does the first filtering release need full AdGuard DNS rule/action compatibility? The latter is a materially larger engine/policy contract. Start with OISD/domain/hosts subscriptions plus explicit exceptions, or require the additional patterns and `$badfilter` found in the current AdGuard DNS filter before accepting that subscription? The prototype does not decide that trade-off. The compact representation is the recommended owned candidate; the naive trie spends too much memory for its small p99 win. Blocked-answer behavior (NXDOMAIN, NODATA or null addresses), negative TTL and response-CNAME filtering still need their own correctness contract before implementing the filtering service. 3. **Recommend a single shared list snapshot per DNS process.** Should filtering apply only to an explicit mesh DNS service, with system-wide adoption opt-in, and global allow/deny overrides first? Per-client policies should reuse the authenticated mesh identity and the same snapshot; untrusted source IP alone must not grant a policy exception. Should reload failures retain last-good rules and continue serving, with a visible refresh error? 4. **Recommend measuring a zone-aware adapter over Hickory transports for slice 3 if the measured footprint is acceptable.** The high-level Resolver follows CNAMEs into the configured upstream, so initial-QNAME dispatch alone violates the mesh privacy boundary. Require local zone policy on every chase and an explicit wire-response contract before integration; its current cache/latency numbers are baselines. This reuses TCP fallback and encrypted transports while keeping mesh routing explicit. Prefer owner-set authenticated DoH/DoT upstreams, ordered explicitly, no automatic downgrade to plaintext. Is local DNSSEC validation required in that slice, or a separately grilled follow-up? Do not equate DoH with DNSSEC. 5. **Recommend deferring public DoH/DoT/DoQ serving and persistent query history.** Does a bounded diagnostic view plus counters satisfy the first troubleshooting need? Persistent history and public listener exposure need separate privacy, retention and access decisions; neither is necessary to resolve mesh names. ## Verification The full `scripts/verify fast` run on compute01 exited 1: all twelve Rust gate rows passed, including static x86 and aarch64 builds, but one existing frontend layout timing assertion failed (363 pass, 1 fail in that tracer batch). `layout.test.ts:75` expected average <25 ms and measured 40.465742 ms. The shipping static sizes exactly matched their existing ceilings: 21,443,824 and 19,629,240 bytes; PT_INTERP absent. No production source or dependency changed in this job. An isolated test from an unmodified base snapshot passed (8 pass, 0 fail). The complete TypeScript gate then also passed on a `git archive origin/dev` snapshot of the base commit using the same installed dependencies (exit 0), including 364/364 tests in the previously failing batch. The timing failure did not reproduce; its precise cause is unproven. This does not turn the original full `verify fast` exit into a pass. No timing threshold was changed. The failed full gate and baseline receipts are attached to [the gate finding](https://git.kayg.org/kayg/infraternal/issues/266#issuecomment-72440). Final receipts: [combined measurements and correctness probes](https://git.kayg.org/attachments/4fe697f2-3665-471f-b923-b971c98d7178), [perf machine before/after](https://git.kayg.org/attachments/f5bb13a9-c840-4fb0-bc58-5d05bac552e1), [frozen-source static build](https://git.kayg.org/attachments/76b41543-16b9-483a-943d-7feb3e4839aa), [full fast gate](https://git.kayg.org/attachments/081587a4-ec84-40d0-9611-8b32a2f398a8), [full baseline TypeScript gate](https://git.kayg.org/attachments/78498a61-3a37-4d03-9775-3a61436e0125). The isolated harness formatting check passed. The build target directory was deleted after preserving binaries and receipts; the measurement runners removed their fixture processes and temporary certificate material before releasing the perf lock. No foundation was selected and no production dependency changed. Final harness/report commit: [`11bbef334cd53d62b08c8ab297b1beec311ecfa8`](https://git.kayg.org/kayg/infraternal/commit/11bbef334cd53d62b08c8ab297b1beec311ecfa8), branch [`research-dns-filtering`](https://git.kayg.org/kayg/infraternal/src/branch/research-dns-filtering/research/dns-filtering). Research only; owner choices remain open.
Author
Owner

J48 feature-on build finding and resolution (dev-02, current pushed head 10e8ab5e0): the first configuration run exposed two compiler errors in the suffix update: the plugin called the mesh crate's pub(crate) validate_dns across a crate boundary, and simple-dns::Name::new_unchecked takes &str, not String. Making the existing canonical validator public for cross-crate reuse and converting the borrowed Name with into_owned() resolved these without duplicating validation or leaking memory. The next compile showed two test expectations with trailing dots; the pinned library's Display deliberately formats parsed names without a final dot, so those assertions now match its output.

Evidence: nix develop -c ~/infraternal-ops/build-slot nice -n10 cargo test -p infraternal-plugin-mesh --features agent,server,mesh-dns-spike --test configuration -j3 completed 16 passed; 0 failed; 0 ignored on dev-02. Full feature-on, compile-out, root namespace, and compute01 fast-gate verification remain in progress.

J48 feature-on build finding and resolution (dev-02, current pushed head `10e8ab5e0`): the first configuration run exposed two compiler errors in the suffix update: the plugin called the mesh crate's `pub(crate) validate_dns` across a crate boundary, and `simple-dns::Name::new_unchecked` takes `&str`, not `String`. Making the existing canonical validator public for cross-crate reuse and converting the borrowed `Name` with `into_owned()` resolved these without duplicating validation or leaking memory. The next compile showed two test expectations with trailing dots; the pinned library's `Display` deliberately formats parsed names without a final dot, so those assertions now match its output. Evidence: `nix develop -c ~/infraternal-ops/build-slot nice -n10 cargo test -p infraternal-plugin-mesh --features agent,server,mesh-dns-spike --test configuration -j3` completed `16 passed; 0 failed; 0 ignored` on dev-02. Full feature-on, compile-out, root namespace, and compute01 fast-gate verification remain in progress.
Author
Owner

R1 additional correctness finding: do not connect Hickory's high-level stub resolver directly to the split-DNS forwarding boundary.

A controlled external response alias0.bench.test CNAME hidden.internal caused Resolver::lookup(A) to send both alias0.bench.test and hidden.internal to the configured external upstream. The returned lookup contained the CNAME and the upstream-supplied A answer for hidden.internal. This is ordinary stub CNAME chasing, not an upstream-library vulnerability; the isolated harness has no mesh-zone policy. It demonstrates why filtering only the initial question is insufficient for D165 and the owner's reachability-gated name visibility.

For slice 3 I recommend a zone-aware forwarding adapter over the measured transport/protocol stack, with this CNAME case in its contract. A packet-level client that returns the alias for normal split-DNS routing, or a dispatcher applied to every chased name with the authenticated asker's visibility, needs a follow-up integration measurement. The high-level Resolver cache numbers are a baseline, not proof of a production-ready split-DNS proxy. In particular, its Lookup API is not a transparent wire response API: response headers, authority/negative answers, EDNS and DNSSEC flags require an explicit server contract.

The probe ran through perf-run, exited 0, and cleaned its fixture and test keys. Both names are synthetic; no actual mesh name was queried. The report will include this limitation and the owner grill now includes the forwarding API/zone-boundary choice. No production code is changed.

R1 additional correctness finding: do not connect Hickory's high-level stub resolver directly to the split-DNS forwarding boundary. A controlled external response `alias0.bench.test CNAME hidden.internal` caused `Resolver::lookup(A)` to send both `alias0.bench.test` and `hidden.internal` to the configured external upstream. The returned lookup contained the CNAME and the upstream-supplied A answer for hidden.internal. This is ordinary stub CNAME chasing, not an upstream-library vulnerability; the isolated harness has no mesh-zone policy. It demonstrates why filtering only the initial question is insufficient for D165 and the owner's reachability-gated name visibility. For slice 3 I recommend a zone-aware forwarding adapter over the measured transport/protocol stack, with this CNAME case in its contract. A packet-level client that returns the alias for normal split-DNS routing, or a dispatcher applied to every chased name with the authenticated asker's visibility, needs a follow-up integration measurement. The high-level Resolver cache numbers are a baseline, not proof of a production-ready split-DNS proxy. In particular, its Lookup API is not a transparent wire response API: response headers, authority/negative answers, EDNS and DNSSEC flags require an explicit server contract. The probe ran through perf-run, exited 0, and cleaned its fixture and test keys. Both names are synthetic; no actual mesh name was queried. The report will include this limitation and the owner grill now includes the forwarding API/zone-boundary choice. No production code is changed.
Author
Owner

J48 verification update (branch mesh-dns): feature-on all-targets exited 0 on dev-02, with 16/16 configuration policy tests and 9/9 DNS tests passing; the ignored namespace test was run separately as root through cua + build-slot and passed (1 passed; 0 failed; 0 ignored; 9 filtered out). The existing review_mesh_view_truth_r3::a_non_owner_cannot_confirm_the_servers_hidden_hostname passed in the feature-on and compile-out suites. Compile-out all-targets exited 0. Feature-on clippy first found an avoidable suffix allocation and then a denied expect_used lint; both are fixed, and the final clippy run exited 0. The DNS fuzz target cargo check --locked also exited 0 using J48's single target directory.

Next: squash WIP commits into one implementation commit, rerun feature-on and compile-out tests plus the root namespace test on that head, run scripts/verify fast on compute01, report the static size delta, and clean J48 target directories.

J48 verification update (branch `mesh-dns`): feature-on all-targets exited 0 on dev-02, with 16/16 configuration policy tests and 9/9 DNS tests passing; the ignored namespace test was run separately as root through `cua` + `build-slot` and passed (`1 passed; 0 failed; 0 ignored; 9 filtered out`). The existing `review_mesh_view_truth_r3::a_non_owner_cannot_confirm_the_servers_hidden_hostname` passed in the feature-on and compile-out suites. Compile-out all-targets exited 0. Feature-on clippy first found an avoidable suffix allocation and then a denied `expect_used` lint; both are fixed, and the final clippy run exited 0. The DNS fuzz target `cargo check --locked` also exited 0 using J48's single target directory. Next: squash WIP commits into one implementation commit, rerun feature-on and compile-out tests plus the root namespace test on that head, run `scripts/verify fast` on compute01, report the static size delta, and clean J48 target directories.
Author
Owner

J48 checkpoint: WIP commits are squashed and pushed as 471c9ebf6, based on 85a08d106 (rebased onto origin/dev f4b61cd6c3f52078cad7573c5367d7a6e75f84f0). On this exact tree, dev-02 feature-on all-targets exited 0 (configuration 16/16, DNS 9/9 plus the existing mesh suites); compile-out all-targets exited 0. The privacy test a_non_owner_cannot_confirm_the_servers_hidden_hostname passed in both modes. Root namespace evidence: running 1 test; test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok; test result: ok. 1 passed; 0 failed; 0 ignored; 9 filtered out; finished in 0.21s. ip netns list is empty after cleanup. Feature-on clippy and the locked DNS fuzz target check passed on the same source tree.

Next: complete scripts/verify fast on compute01, record the static binary delta against the requested base, post the exact gate evidence, and remove J48 target directories.

J48 checkpoint: WIP commits are squashed and pushed as `471c9ebf6`, based on `85a08d106` (rebased onto `origin/dev` `f4b61cd6c3f52078cad7573c5367d7a6e75f84f0`). On this exact tree, dev-02 feature-on all-targets exited 0 (configuration 16/16, DNS 9/9 plus the existing mesh suites); compile-out all-targets exited 0. The privacy test `a_non_owner_cannot_confirm_the_servers_hidden_hostname` passed in both modes. Root namespace evidence: `running 1 test`; `test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok`; `test result: ok. 1 passed; 0 failed; 0 ignored; 9 filtered out; finished in 0.21s`. `ip netns list` is empty after cleanup. Feature-on clippy and the locked DNS fuzz target check passed on the same source tree. Next: complete `scripts/verify fast` on compute01, record the static binary delta against the requested base, post the exact gate evidence, and remove J48 target directories.
Author
Owner

R1 checkpoint: research branch research-dns-filtering at ae885ed8a. Full scripts/verify fast is running on compute01 through build-slot; workspace tests (913s), alternate shim tests (167s), cargo-deny and architecture checks have completed, now in static builds. Log: /home/claude/.cache/codex-scratch/r1-dns/verify-fast.log. One target: /home/claude/worktrees/r1-dns/target.

The report in comment 72371 includes the measured CNAME privacy limitation and link to the query trace. Frozen-source harness rebuild and one final perf-run are queued after the gate, then final report/head/receipts and target cleanup. No production dependency change or foundation selection.

R1 checkpoint: research branch `research-dns-filtering` at `ae885ed8a`. Full `scripts/verify fast` is running on compute01 through build-slot; workspace tests (913s), alternate shim tests (167s), cargo-deny and architecture checks have completed, now in static builds. Log: `/home/claude/.cache/codex-scratch/r1-dns/verify-fast.log`. One target: `/home/claude/worktrees/r1-dns/target`. The report in comment 72371 includes the measured CNAME privacy limitation and link to the query trace. Frozen-source harness rebuild and one final perf-run are queued after the gate, then final report/head/receipts and target cleanup. No production dependency change or foundation selection.
Author
Owner

R1 crash-safety checkpoint pushed: research-dns-filtering at 1bcf8b6c0. scripts/verify fast has completed the x86 static build: PT_INTERP: absent; size: 21443824 bytes; cap: 21443824 bytes; delta: +0 bytes; key memory initialization=1. The aarch64 static build is running next. Same gate log and single target as comment 72406. No failures reported so far, but no final pass claim until the gate exits. Final frozen-source harness rebuild/perf receipt and cleanup follow.

R1 crash-safety checkpoint pushed: `research-dns-filtering` at `1bcf8b6c0`. `scripts/verify fast` has completed the x86 static build: `PT_INTERP: absent; size: 21443824 bytes; cap: 21443824 bytes; delta: +0 bytes`; key memory initialization=1. The aarch64 static build is running next. Same gate log and single target as comment 72406. No failures reported so far, but no final pass claim until the gate exits. Final frozen-source harness rebuild/perf receipt and cleanup follow.
Author
Owner

R1 gate finding: scripts/verify fast exited 1 in the existing tracer layout timing assertion. All 12 Rust rows passed, including both static builds at exactly the recorded size ceilings. Tracer first batch: 363 pass, 1 fail; layout.test.ts:75 expected average < 25 ms, received 40.465742000000134 ms across ten layouts.

The branch changes only research/dns-filtering/; production source matches the base. An isolated test of a git archive origin/dev baseline snapshot passed (8 pass, 0 fail), so I am not claiming the full-suite failure reproduced yet. Full baseline TypeScript gate is queued through build-slot after the frozen-source harness rebuild (same one target). Logs remain in ~/.cache/codex-scratch/r1-dns/; no production fix or threshold change is being made in this research job.

R1 gate finding: `scripts/verify fast` exited 1 in the existing tracer layout timing assertion. All 12 Rust rows passed, including both static builds at exactly the recorded size ceilings. Tracer first batch: `363 pass`, `1 fail`; `layout.test.ts:75` expected average `< 25` ms, received `40.465742000000134` ms across ten layouts. The branch changes only `research/dns-filtering/`; production source matches the base. An isolated test of a `git archive origin/dev` baseline snapshot passed (`8 pass`, `0 fail`), so I am not claiming the full-suite failure reproduced yet. Full baseline TypeScript gate is queued through build-slot after the frozen-source harness rebuild (same one target). Logs remain in `~/.cache/codex-scratch/r1-dns/`; no production fix or threshold change is being made in this research job.
Author
Owner

R1 crash-safety checkpoint: research-dns-filtering at 27fca5b52. The report now explicitly records the failed frontend timing assertion and cache/alias mesh-zone boundary. Full gate receipts are attached to comment 72440. The final frozen-source harness rebuild plus full baseline TypeScript comparison has waited for build capacity since the gate finished, now second in the live FIFO queue (initial position 10). No orphan executables from this job's target remain; disk has >100 GiB free. Still one target /home/claude/worktrees/r1-dns/target, to delete after final measurements. No foundation choice or production changes.

R1 crash-safety checkpoint: `research-dns-filtering` at `27fca5b52`. The report now explicitly records the failed frontend timing assertion and cache/alias mesh-zone boundary. Full gate receipts are attached to comment 72440. The final frozen-source harness rebuild plus full baseline TypeScript comparison has waited for build capacity since the gate finished, now second in the live FIFO queue (initial position 10). No orphan executables from this job's target remain; disk has >100 GiB free. Still one target `/home/claude/worktrees/r1-dns/target`, to delete after final measurements. No foundation choice or production changes.
Author
Owner

J48 progress (2026-11-01): the policy clarification audit and dev-02 feature-on, compile-out, privacy, and root namespace checks are complete on pushed head 471c9ebf6. I invalidated the first compute01 scripts/verify fast attempt after the base-size job acquired a second machine slot and switched the same worktree to f4b61cd6 while the gate was still running. I stopped both J48 process groups, restored the worktree to 471c9ebf6, confirmed no J48 processes remained, and removed the mixed target and test state. No source changes were made.

A fresh scripts/verify fast is queued through build-slot at position 10 on compute01. Its log is /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rerun.log. The interrupted logs are retained beside it. I will run the base static-size build only after the fast gate exits, then remove the J48 target directories.

J48 progress (2026-11-01): the policy clarification audit and dev-02 feature-on, compile-out, privacy, and root namespace checks are complete on pushed head `471c9ebf6`. I invalidated the first compute01 `scripts/verify fast` attempt after the base-size job acquired a second machine slot and switched the same worktree to `f4b61cd6` while the gate was still running. I stopped both J48 process groups, restored the worktree to `471c9ebf6`, confirmed no J48 processes remained, and removed the mixed target and test state. No source changes were made. A fresh `scripts/verify fast` is queued through `build-slot` at position 10 on compute01. Its log is `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rerun.log`. The interrupted logs are retained beside it. I will run the base static-size build only after the fast gate exits, then remove the J48 target directories.
Author
Owner

R1 verification update: the complete ts_verify_fast gate passed (exit 0) on an unmodified git archive origin/dev snapshot of base f4b61cd6c3f52078cad7573c5367d7a6e75f84f0, using the same installed dependencies. The previously failing batch was 364 pass, 0 fail. Receipt attached to comment 72440: https://git.kayg.org/attachments/78498a61-3a37-4d03-9775-3a61436e0125 . The isolated baseline layout test had also passed. Thus the initial timing failure did not reproduce; I am not attributing a proven cause or relabeling the original full fast exit 1 as a pass.

All frozen-source harness configurations rebuilt successfully, no PT_INTERP. Final transport static bytes: own UDP 709896; own DoH 2275984; Hickory plain 1686760; Hickory encrypted/DNSSEC 3798032. Filter executable sizes reproduced exactly. Isolated formatting check passed. Compute target /home/claude/worktrees/r1-dns/target was deleted after binaries/receipts were saved; no further builds are pending. Final perf-run refresh is waiting for the perf VM's existing measurement lock, then report/head publication and process cleanup remain. No foundation selected.

R1 verification update: the complete `ts_verify_fast` gate passed (exit 0) on an unmodified `git archive origin/dev` snapshot of base `f4b61cd6c3f52078cad7573c5367d7a6e75f84f0`, using the same installed dependencies. The previously failing batch was `364 pass`, `0 fail`. Receipt attached to comment 72440: https://git.kayg.org/attachments/78498a61-3a37-4d03-9775-3a61436e0125 . The isolated baseline layout test had also passed. Thus the initial timing failure did not reproduce; I am not attributing a proven cause or relabeling the original full fast exit 1 as a pass. All frozen-source harness configurations rebuilt successfully, no PT_INTERP. Final transport static bytes: own UDP 709896; own DoH 2275984; Hickory plain 1686760; Hickory encrypted/DNSSEC 3798032. Filter executable sizes reproduced exactly. Isolated formatting check passed. Compute target `/home/claude/worktrees/r1-dns/target` was deleted after binaries/receipts were saved; no further builds are pending. Final perf-run refresh is waiting for the perf VM's existing measurement lock, then report/head publication and process cleanup remain. No foundation selected.
Author
Owner

R1 finished: branch research-dns-filtering, head 11bbef334cd53d62b08c8ab297b1beec311ecfa8. Final comparison and staged owner grill. Harness and report source.

Measured 495,009 raw rules across AdGuard DNS, OISD big and a hosts list. Final compact subset: +63,424 static bytes, 31.26 MiB warm RSS, 174.4 ms compile, 0.721/4.038 µs lookup p50/p99. Browser-engine DNS mismatches and utilvo's coverage loss are documented; MPL secondary-license compatibility is sourced precisely. Final transport configurations, cache scale, TC fallback, DNSSEC smoke and traced CNAME boundary are measured and attached.

Recommendation remains mesh zone → strict filtering subset → zone-aware encrypted upstream; foundations belong to kayg. No production dependency or decision ledger changed. No merge/deploy and issue remains open.

scripts/verify fast exited 1. Verbatim Rust summary:

+---------------------------+--------------+
| Rust gate                 | Result       |
+---------------------------+--------------+
| fmt                       | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| real-binary fixture paths | PASS         |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+

Verbatim final failing tracer batch:

1 tests failed:
(fail) 1,000 nodes lay out in a few milliseconds [448.91ms]

 363 pass
 1 fail
 21703 expect() calls
Ran 364 tests across 60 files. [38.67s]

The full baseline TypeScript gate subsequently exited 0 on an unmodified base snapshot, including 364/364 tests in that batch. The initial timing failure did not reproduce and its cause is unproven; this does not relabel the initial fast exit as passing. Both full logs are attached. Both shipping static sizes exactly matched their ceilings, PT_INTERP absent.

Final source check, verbatim:

repository artifact gate: PASS (17 changed path(s), merge base f4b61cd6c3f5)
target-absent

Final perf-run, verbatim:

command: bash -c bash measure.sh && bash cname-probe.sh && bash dnssec-probe.sh
cold: 0  exit: 0
wall: 15.042s
steal during run: 35 of 6055 jiffies (all cpus)

Final combined measurements/probes, machine before/after, frozen build, full fast gate, baseline TS gate.

Cleanup confirmed: /home/claude/worktrees/r1-dns/target deleted; no research workload executables or fixture directories remain on perf-01; temporary fixture keys removed by traps; all our build and measurement commands have exited. Control worktree is clean. No research blocker remains; the failed initial repository gate and owner foundation questions are explicit handoff items.

R1 finished: branch `research-dns-filtering`, head `11bbef334cd53d62b08c8ab297b1beec311ecfa8`. [Final comparison and staged owner grill](https://git.kayg.org/kayg/infraternal/issues/266#issuecomment-72371). [Harness and report source](https://git.kayg.org/kayg/infraternal/src/branch/research-dns-filtering/research/dns-filtering). Measured 495,009 raw rules across AdGuard DNS, OISD big and a hosts list. Final compact subset: +63,424 static bytes, 31.26 MiB warm RSS, 174.4 ms compile, 0.721/4.038 µs lookup p50/p99. Browser-engine DNS mismatches and utilvo's coverage loss are documented; MPL secondary-license compatibility is sourced precisely. Final transport configurations, cache scale, TC fallback, DNSSEC smoke and traced CNAME boundary are measured and attached. Recommendation remains mesh zone → strict filtering subset → zone-aware encrypted upstream; foundations belong to kayg. No production dependency or decision ledger changed. No merge/deploy and issue remains open. `scripts/verify fast` exited **1**. Verbatim Rust summary: ```text +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | fmt | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | static builds | PASS | +---------------------------+--------------+ ``` Verbatim final failing tracer batch: ```text 1 tests failed: (fail) 1,000 nodes lay out in a few milliseconds [448.91ms] 363 pass 1 fail 21703 expect() calls Ran 364 tests across 60 files. [38.67s] ``` The full baseline TypeScript gate subsequently exited 0 on an unmodified base snapshot, including 364/364 tests in that batch. The initial timing failure did not reproduce and its cause is unproven; this does not relabel the initial fast exit as passing. Both full logs are attached. Both shipping static sizes exactly matched their ceilings, PT_INTERP absent. Final source check, verbatim: ```text repository artifact gate: PASS (17 changed path(s), merge base f4b61cd6c3f5) target-absent ``` Final perf-run, verbatim: ```text command: bash -c bash measure.sh && bash cname-probe.sh && bash dnssec-probe.sh cold: 0 exit: 0 wall: 15.042s steal during run: 35 of 6055 jiffies (all cpus) ``` [Final combined measurements/probes](https://git.kayg.org/attachments/4fe697f2-3665-471f-b923-b971c98d7178), [machine before/after](https://git.kayg.org/attachments/f5bb13a9-c840-4fb0-bc58-5d05bac552e1), [frozen build](https://git.kayg.org/attachments/76b41543-16b9-483a-943d-7feb3e4839aa), [full fast gate](https://git.kayg.org/attachments/081587a4-ec84-40d0-9611-8b32a2f398a8), [baseline TS gate](https://git.kayg.org/attachments/78498a61-3a37-4d03-9775-3a61436e0125). Cleanup confirmed: `/home/claude/worktrees/r1-dns/target` deleted; no research workload executables or fixture directories remain on perf-01; temporary fixture keys removed by traps; all our build and measurement commands have exited. Control worktree is clean. No research blocker remains; the failed initial repository gate and owner foundation questions are explicit handoff items.
Author
Owner

J48 progress (2026-11-01): the clean compute01 scripts/verify fast rerun remains queued through build-slot (initial position 10); it has not started compiling, and no target has been created. Log: /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rerun.log. The worktree remains clean at 471c9ebf6. I will measure the f4b61cd6 baseline only after the gate exits, then remove all J48 target directories.

J48 progress (2026-11-01): the clean compute01 `scripts/verify fast` rerun remains queued through `build-slot` (initial position 10); it has not started compiling, and no target has been created. Log: `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rerun.log`. The worktree remains clean at `471c9ebf6`. I will measure the `f4b61cd6` baseline only after the gate exits, then remove all J48 target directories.
Author
Owner

J48 progress (2026-11-01): the clean compute01 scripts/verify fast rerun has waited about 30 minutes for a build slot and remains queued at its initial position 10. It has not created a target or started compiling. The branch/worktree is still clean at 471c9ebf6; log: /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rerun.log. The base-size build remains unstarted to avoid touching the shared worktree before the gate completes.

J48 progress (2026-11-01): the clean compute01 `scripts/verify fast` rerun has waited about 30 minutes for a build slot and remains queued at its initial position 10. It has not created a target or started compiling. The branch/worktree is still clean at `471c9ebf6`; log: `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rerun.log`. The base-size build remains unstarted to avoid touching the shared worktree before the gate completes.
Author
Owner

J48 progress (2026-11-01): the clean compute01 scripts/verify fast rerun remains queued at position 10, and available disk has fallen to 16 GiB (the configured build-slot minimum is 25 GiB). The slot guard is keeping the command from starting. No J48 target exists and the worktree remains clean at 471c9ebf6. Log: /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rerun.log. Waiting for compute01 capacity to recover before running the gate or base-size build.

J48 progress (2026-11-01): the clean compute01 `scripts/verify fast` rerun remains queued at position 10, and available disk has fallen to 16 GiB (the configured build-slot minimum is 25 GiB). The slot guard is keeping the command from starting. No J48 target exists and the worktree remains clean at `471c9ebf6`. Log: `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rerun.log`. Waiting for compute01 capacity to recover before running the gate or base-size build.
Author
Owner

Owner decisions (kayg, 2026-11-01), DNS stages (R1 comment 72371): Slice 1 = mesh names only (agreed). Slice 2 = our own compact domain-index engine, performance measured, with a documented DNS-rule subset and visible diagnostics for unsupported rules; must handle OISD (domains/hosts/ABP-style ||domain^ formats), hosts files and plain domain lists. Slice 3 upstream = hickory-resolver (owner: "is hickory mature, updated? then yes" — confirm maturity/release cadence in the slice-3 plan).

**Owner decisions (kayg, 2026-11-01), DNS stages (R1 comment 72371):** Slice 1 = mesh names only (agreed). Slice 2 = **our own compact domain-index engine**, performance measured, with a documented DNS-rule subset and visible diagnostics for unsupported rules; must handle OISD (domains/hosts/ABP-style `||domain^` formats), hosts files and plain domain lists. Slice 3 upstream = **hickory-resolver** (owner: "is hickory mature, updated? then yes" — confirm maturity/release cadence in the slice-3 plan).
Author
Owner

J48 progress: the clean scripts/verify fast rerun has left the compute01 build-slot queue and is compiling from the detached 471c9ebf6eaf5089fdc0284646873e11313f8c6c worktree. It is the only J48 build using that worktree and target; the base static-size comparison has not started. The run log is /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rerun.log. After the gate exits I will measure the base size, record the delta, post final evidence, and remove the J48 target directories. The user-identity/group policy gap and owner question are already recorded on #266 and cross-referenced on #173.

J48 progress: the clean `scripts/verify fast` rerun has left the compute01 build-slot queue and is compiling from the detached `471c9ebf6eaf5089fdc0284646873e11313f8c6c` worktree. It is the only J48 build using that worktree and target; the base static-size comparison has not started. The run log is `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rerun.log`. After the gate exits I will measure the base size, record the delta, post final evidence, and remove the J48 target directories. The user-identity/group policy gap and owner question are already recorded on #266 and cross-referenced on #173.
Author
Owner

J48 finding during the clean compute01 gate: the workspace shim test target reports path must be shorter than SUN_LEN in four tests. The gate wrapper sets INFRATERNAL_TEST_STATE_ROOT=/home/claude/.cache/codex-scratch/j48-mesh-dns/test-state (57 bytes); crates/infraternal/tests/shim.rs appends s<N>/sessions/<32-byte-id>.sock, so the resulting path is 108 bytes at s10, which exceeds Linux's AF_UNIX pathname limit. This is an overlong isolated test-state path, not a DNS code failure. I am preserving the full gate result, then will rerun the shim target and fast gate with short per-job state roots under /tmp; no source changes are planned for this environment issue.

J48 finding during the clean compute01 gate: the workspace `shim` test target reports `path must be shorter than SUN_LEN` in four tests. The gate wrapper sets `INFRATERNAL_TEST_STATE_ROOT=/home/claude/.cache/codex-scratch/j48-mesh-dns/test-state` (57 bytes); `crates/infraternal/tests/shim.rs` appends `s<N>/sessions/<32-byte-id>.sock`, so the resulting path is 108 bytes at `s10`, which exceeds Linux's AF_UNIX pathname limit. This is an overlong isolated test-state path, not a DNS code failure. I am preserving the full gate result, then will rerun the shim target and fast gate with short per-job state roots under `/tmp`; no source changes are planned for this environment issue.
Author
Owner

Owner direction (kayg, 2026-11-01): the server's public hostname becomes the mesh DNS suffix automatically. Example: the server is at ssh.kayg.org, so every machine is automatically <machine>.ssh.kayg.org (no manual suffix setup). Orchestrator proposal for the details (owner to confirm): keep <machine>.internal as an always-available alias (D165); these names resolve via split DNS inside the mesh only (not published in public DNS), gated by access grants as decided; a wildcard *.ssh.kayg.org certificate via DNS-01 lets the reverse proxy serve HTTPS on them.
Later (owner): once the reverse proxy and the decentralised canvas (untrusted server; see #167) exist, the canvas is served automatically at that address. Recorded as a roadmap item, not part of slice 1.

**Owner direction (kayg, 2026-11-01): the server's public hostname becomes the mesh DNS suffix automatically.** Example: the server is at `ssh.kayg.org`, so every machine is automatically `<machine>.ssh.kayg.org` (no manual suffix setup). Orchestrator proposal for the details (owner to confirm): keep `<machine>.internal` as an always-available alias (D165); these names resolve via split DNS inside the mesh only (not published in public DNS), gated by access grants as decided; a wildcard `*.ssh.kayg.org` certificate via DNS-01 lets the reverse proxy serve HTTPS on them. **Later (owner):** once the reverse proxy and the decentralised canvas (untrusted server; see #167) exist, the canvas is served automatically at that address. Recorded as a roadmap item, not part of slice 1.
Author
Owner

J48 continues on mesh-dns at 471c9ebf6, based on origin/dev f4b61cd6c3f52078cad7573c5367d7a6e75f84f0.

Finding for the latest suffix direction: ServerConfig.public_origin is parsed in runtime::build_router_core, but the current WebSocket/plugin constructor passes only the pool, so configuration::compile cannot derive the public-host suffix. The node coordinator's changed comparison also omits dns_suffix; an origin change therefore needs to advance the accepted revision and travel through the existing complete snapshot path. I am threading the existing origin through those constructors, keeping explicit MeshSettings.dns_suffix authoritative, and keeping .internal as a resolver alias. No TLS, proxy, inventory, or mesh-view API changes are in scope.

The earlier clean compute01 scripts/verify fast is still running from 471c9ebf6; log: /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rerun.log. I will not touch its worktree or target until it exits.

J48 continues on `mesh-dns` at `471c9ebf6`, based on `origin/dev` `f4b61cd6c3f52078cad7573c5367d7a6e75f84f0`. Finding for the latest suffix direction: `ServerConfig.public_origin` is parsed in `runtime::build_router_core`, but the current WebSocket/plugin constructor passes only the pool, so `configuration::compile` cannot derive the public-host suffix. The node coordinator's `changed` comparison also omits `dns_suffix`; an origin change therefore needs to advance the accepted revision and travel through the existing complete snapshot path. I am threading the existing origin through those constructors, keeping explicit `MeshSettings.dns_suffix` authoritative, and keeping `.internal` as a resolver alias. No TLS, proxy, inventory, or mesh-view API changes are in scope. The earlier clean compute01 `scripts/verify fast` is still running from `471c9ebf6`; log: `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rerun.log`. I will not touch its worktree or target until it exits.
Author
Owner

TDD RED on dev-02-std through build-slot: origin_derived_suffix_and_internal_alias_both_resolve failed against the pre-alias matcher, as expected. The derived-zone lookup passed; .internal returned no authoritative response, failing at tests/dns.rs:192 (0 passed; 1 failed). I am restoring the suffix-list matcher and will rerun the focused DNS suite before the full feature-on/off checks.

TDD RED on dev-02-std through `build-slot`: `origin_derived_suffix_and_internal_alias_both_resolve` failed against the pre-alias matcher, as expected. The derived-zone lookup passed; `.internal` returned no authoritative response, failing at `tests/dns.rs:192` (`0 passed; 1 failed`). I am restoring the suffix-list matcher and will rerun the focused DNS suite before the full feature-on/off checks.
Author
Owner

J48 compute01 gate finding on pushed head 471c9ebf6: the full scripts/verify fast run exited 1 because 14 unchanged crates/infraternal/tests/shim.rs cases failed to create AF_UNIX sockets with path must be shorter than SUN_LEN; 10 shim cases passed. Both static builds passed their existing rows (x86_64 21,443,824 bytes; aarch64 19,629,240 bytes). The gate wrapper's 57-byte state root makes the longest fixture path exceed the kernel limit. I will rerun the final gate with both state-root variables set to a short directory on /home so sockets fit and restart fixtures retain same-filesystem behavior. Log: /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rerun.log.

J48 compute01 gate finding on pushed head `471c9ebf6`: the full `scripts/verify fast` run exited 1 because 14 unchanged `crates/infraternal/tests/shim.rs` cases failed to create AF_UNIX sockets with `path must be shorter than SUN_LEN`; 10 shim cases passed. Both static builds passed their existing rows (x86_64 21,443,824 bytes; aarch64 19,629,240 bytes). The gate wrapper's 57-byte state root makes the longest fixture path exceed the kernel limit. I will rerun the final gate with both state-root variables set to a short directory on `/home` so sockets fit and restart fixtures retain same-filesystem behavior. Log: `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rerun.log`.
Author
Owner

Feature-on clippy finding (dev-02): cargo clippy --all-targets --features agent,server,mesh-dns-spike -D warnings found needless_pass_by_value in MeshPlugin::with_mesh_service and doc_markdown for WireGuard in the new configuration docs. I changed the helper to borrow the pool while preserving the existing single pool clone into MeshService, and marked the code identifier in the comment. I am rerunning the same lint gate.

Feature-on clippy finding (dev-02): `cargo clippy --all-targets --features agent,server,mesh-dns-spike -D warnings` found `needless_pass_by_value` in `MeshPlugin::with_mesh_service` and `doc_markdown` for `WireGuard` in the new configuration docs. I changed the helper to borrow the pool while preserving the existing single pool clone into `MeshService`, and marked the code identifier in the comment. I am rerunning the same lint gate.
Author
Owner

The final dev-02 gate sequence stopped at its first cargo fmt --all -- --check: rustfmt requested the pool-options method chain in socket.rs be kept on one line. I applied that formatting-only adjustment and am restarting the complete sequence (fmt, feature-on/off suites, clippy, and the locked DNS fuzz target check).

The final dev-02 gate sequence stopped at its first `cargo fmt --all -- --check`: rustfmt requested the pool-options method chain in `socket.rs` be kept on one line. I applied that formatting-only adjustment and am restarting the complete sequence (fmt, feature-on/off suites, clippy, and the locked DNS fuzz target check).
Author
Owner

Compile-out finding (dev-02): cargo test --features agent,server --all-targets passes but reports unused variable: default_dns_suffix in configuration::compile_inner. The suffix is only consumed under mesh-dns-spike; I will explicitly consume the argument in compile-out builds, then rerun feature-on/off tests and the lint/check sequence.

Compile-out finding (dev-02): `cargo test --features agent,server --all-targets` passes but reports `unused variable: default_dns_suffix` in `configuration::compile_inner`. The suffix is only consumed under `mesh-dns-spike`; I will explicitly consume the argument in compile-out builds, then rerun feature-on/off tests and the lint/check sequence.
Author
Owner

Crash-safety checkpoint pushed on mesh-dns at 9edd77392 (WIP), based on 471c9ebf6 / origin/dev f4b61cd6c3f52078cad7573c5367d7a6e75f84f0. The suffix path now flows from ServerConfig.public_origin through WebSocket/plugin construction into Store snapshots; default-zone changes advance the existing config revision, explicit suffixes win, and DNS retains .internal as an alias. The DNS foundation remains behind mesh-dns-spike.

Dev-02 final-sequence logs: fmt passed; feature-on and compile-out all-targets passed; feature-on clippy and the locked DNS fuzz check passed. Compile-out emitted one unused-argument warning, so I am making that path explicit and rerunning the feature suites. Root namespace and compute01 scripts/verify fast remain pending. Logs: /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/.

Crash-safety checkpoint pushed on `mesh-dns` at `9edd77392` (WIP), based on `471c9ebf6` / `origin/dev` `f4b61cd6c3f52078cad7573c5367d7a6e75f84f0`. The suffix path now flows from `ServerConfig.public_origin` through WebSocket/plugin construction into `Store` snapshots; default-zone changes advance the existing config revision, explicit suffixes win, and DNS retains `.internal` as an alias. The DNS foundation remains behind `mesh-dns-spike`. Dev-02 final-sequence logs: fmt passed; feature-on and compile-out all-targets passed; feature-on clippy and the locked DNS fuzz check passed. Compile-out emitted one unused-argument warning, so I am making that path explicit and rerunning the feature suites. Root namespace and compute01 `scripts/verify fast` remain pending. Logs: `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/`.
Author
Owner

J48 progress: suffix changes are pushed on mesh-dns at b93f16955, based on origin/dev f4b61cd6c. The feature-off unused-argument fix is included. I am syncing this head to the VM worktrees for final feature-on/off checks and the compute01 fast gate; the node/tag policy coverage and unexpressible user-grant question remain recorded here and cross-referenced on #173. Target directories will be removed after the gates.

J48 progress: suffix changes are pushed on mesh-dns at b93f16955, based on origin/dev f4b61cd6c. The feature-off unused-argument fix is included. I am syncing this head to the VM worktrees for final feature-on/off checks and the compute01 fast gate; the node/tag policy coverage and unexpressible user-grant question remain recorded here and cross-referenced on #173. Target directories will be removed after the gates.
Author
Owner

J48 finding: the coordinator model requires a host label, and Store marks its synthetic unnamed fallback with name_source=placeholder. The current DNS compiler would publish that placeholder to a reachable peer as though it were a real hostname. The owner direction says DNS names are the real hostname and must not use the mesh-view unnamed projection, so this slice will keep a placeholder-only node reachable but omit its DNS name until a real name source is available. This uses Store naming provenance and does not change inventory or mesh-view behavior; a regression test is being added.

J48 finding: the coordinator model requires a host label, and Store marks its synthetic unnamed fallback with name_source=placeholder. The current DNS compiler would publish that placeholder to a reachable peer as though it were a real hostname. The owner direction says DNS names are the real hostname and must not use the mesh-view unnamed projection, so this slice will keep a placeholder-only node reachable but omit its DNS name until a real name source is available. This uses Store naming provenance and does not change inventory or mesh-view behavior; a regression test is being added.
Author
Owner

J48 follow-up pushed as ad4f7eee8: a node with Store name_source=placeholder remains in its policy peer map but is omitted from local and peer DNS names. The regression first failed because the reachable unnamed node appeared in peer_names; after the change it passed and unnamed.internal returned NXDOMAIN. Inventory and mesh-view naming remain unchanged. I am syncing this head for fresh feature-on/off, root namespace and compute01 fast-gate runs.

J48 follow-up pushed as ad4f7eee8: a node with Store name_source=placeholder remains in its policy peer map but is omitted from local and peer DNS names. The regression first failed because the reachable unnamed node appeared in peer_names; after the change it passed and unnamed.internal returned NXDOMAIN. Inventory and mesh-view naming remain unchanged. I am syncing this head for fresh feature-on/off, root namespace and compute01 fast-gate runs.
Author
Owner

J48 compile-out finding on ad4f7eee8: feature-on all-targets passed, but compile-out all-targets reported one unused import in the new Store test (rules::TrafficPolicy is only used by the mesh-dns-spike-gated case). I am changing that assertion to a fully qualified path so the default-off build stays warning-free, then will rerun both modes.

J48 compile-out finding on ad4f7eee8: feature-on all-targets passed, but compile-out all-targets reported one unused import in the new Store test (`rules::TrafficPolicy` is only used by the mesh-dns-spike-gated case). I am changing that assertion to a fully qualified path so the default-off build stays warning-free, then will rerun both modes.
Author
Owner

J48 compile-out warning fix is pushed at 8f3197bf5. The new assertion now fully qualifies TrafficPolicy at its feature-gated use site, so compile-out builds no longer import it unused. I am refreshing feature-on/off tests and the compute01 fast gate on 8f3197bf5; the prior ad4 feature-on and clippy runs passed, while compile-out passed with the now-fixed warning.

J48 compile-out warning fix is pushed at 8f3197bf5. The new assertion now fully qualifies TrafficPolicy at its feature-gated use site, so compile-out builds no longer import it unused. I am refreshing feature-on/off tests and the compute01 fast gate on 8f3197bf5; the prior ad4 feature-on and clippy runs passed, while compile-out passed with the now-fixed warning.
Author
Owner

J48 progress on pushed head 8f3197bf5 (based on origin/dev f4b61cd6c): dev-02 feature-on all-targets and compile-out all-targets pass; feature-on clippy, formatting, and the root network-namespace test pass. The existing hidden-hostname mesh-view privacy regression passes with the DNS feature both enabled and disabled.

The required compute01 scripts/verify fast remains active in build-slot, currently queue position 6 with 69 GiB free. Its log is /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-8f.log. Next are the full gate result, fresh static D243 comparison against f4b61cd6c for x86_64 and aarch64, final evidence on this issue, and J48 target cleanup. No size cap is being used to stop the work; the measured delta will be reported.

J48 progress on pushed head `8f3197bf5` (based on `origin/dev` `f4b61cd6c`): dev-02 feature-on all-targets and compile-out all-targets pass; feature-on clippy, formatting, and the root network-namespace test pass. The existing hidden-hostname mesh-view privacy regression passes with the DNS feature both enabled and disabled. The required compute01 `scripts/verify fast` remains active in `build-slot`, currently queue position 6 with 69 GiB free. Its log is `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-8f.log`. Next are the full gate result, fresh static D243 comparison against `f4b61cd6c` for x86_64 and aarch64, final evidence on this issue, and J48 target cleanup. No size cap is being used to stop the work; the measured delta will be reported.
Author
Owner

J48 resumed on branch mesh-dns at pushed head 8f3197bf5; current origin/dev is 37bbda45554f6e603a6856fc23319e17d8a1b14f. I am rebasing the WIP, then will rerun the required VM checks, report the D243 deltas, and post final evidence. No J48 build remains pending from the prior run.

J48 resumed on branch `mesh-dns` at pushed head `8f3197bf5`; current `origin/dev` is `37bbda45554f6e603a6856fc23319e17d8a1b14f`. I am rebasing the WIP, then will rerun the required VM checks, report the D243 deltas, and post final evidence. No J48 build remains pending from the prior run.
Author
Owner

Rebased mesh-dns onto current origin/dev at 37bbda45554f6e603a6856fc23319e17d8a1b14f; new head 8e981eede is pushed. Policy audit of the coordinator confirms name publication follows directional outbound reach from the compiled WireGuard PeerPolicy: tests cover one-way node grants, tag grants, tag membership without a grant, grant revocation/removal, and a non-empty port-limited grant. The current model rejects Endpoint::User as MeshError::UserEndpoint and nodes have no user ownership mapping, so user/device and user-group grants cannot be expressed here. That gap remains an owner question already cross-referenced on #173; no separate DNS policy was added.

Rebased `mesh-dns` onto current `origin/dev` at `37bbda45554f6e603a6856fc23319e17d8a1b14f`; new head `8e981eede` is pushed. Policy audit of the coordinator confirms name publication follows directional outbound reach from the compiled WireGuard `PeerPolicy`: tests cover one-way node grants, tag grants, tag membership without a grant, grant revocation/removal, and a non-empty port-limited grant. The current model rejects `Endpoint::User` as `MeshError::UserEndpoint` and nodes have no user ownership mapping, so user/device and user-group grants cannot be expressed here. That gap remains an owner question already cross-referenced on #173; no separate DNS policy was added.
Author
Owner

J48 rebased validation at 8e981eede (dev-02, all commands through build-slot):

  • cargo fmt --all -- --check: exit 0.
  • Feature-on mesh --all-targets: exit 0; 62 unit tests and the one-way node grant, one-way tag grant, tag-without-grant, grant revocation, custom suffix, default-origin suffix, and .internal alias cases pass.
  • Compile-out mesh --all-targets: exit 0. a_non_owner_cannot_confirm_the_servers_hidden_hostname passes with DNS both enabled and disabled.
  • Clippy in feature-on and compile-out modes and the locked DNS fuzz target check: exit 0.
  • Root namespace test via cua@infraternal-dev-02-std, sudo, and build-slot: test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok; test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 10 filtered out; namespace cleanup: PASS.

Logs are under /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/ on dev-02. The required compute01 scripts/verify fast is still running; I will report its final output and D243 size deltas after it exits.

J48 rebased validation at `8e981eede` (dev-02, all commands through `build-slot`): - `cargo fmt --all -- --check`: exit 0. - Feature-on mesh `--all-targets`: exit 0; 62 unit tests and the one-way node grant, one-way tag grant, tag-without-grant, grant revocation, custom suffix, default-origin suffix, and `.internal` alias cases pass. - Compile-out mesh `--all-targets`: exit 0. `a_non_owner_cannot_confirm_the_servers_hidden_hostname` passes with DNS both enabled and disabled. - Clippy in feature-on and compile-out modes and the locked DNS fuzz target check: exit 0. - Root namespace test via `cua@infraternal-dev-02-std`, `sudo`, and `build-slot`: `test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok`; `test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 10 filtered out`; `namespace cleanup: PASS`. Logs are under `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/` on dev-02. The required compute01 `scripts/verify fast` is still running; I will report its final output and D243 size deltas after it exits.
Author
Owner

Finding from the first rebased compute01 scripts/verify fast run: the full test phase had 7 restart-fixture failures after I set state roots under /tmp. Evidence: df -T /home/claude /tmp shows /home on /dev/sda1 (ext4) and /tmp on tmpfs; tests reported CrossesDevices at restart_tests.rs:469 and PermissionDenied while executing fixture binaries. This is my environment override, not a code change. The gate is continuing through static builds; log: /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rebased.log. I will rerun on this same head with short roots /home/claude/j48t and /home/claude/j48a, keeping socket paths short and fixtures on the worktree filesystem.

Finding from the first rebased compute01 `scripts/verify fast` run: the full test phase had 7 restart-fixture failures after I set state roots under `/tmp`. Evidence: `df -T /home/claude /tmp` shows `/home` on `/dev/sda1` (ext4) and `/tmp` on tmpfs; tests reported `CrossesDevices` at `restart_tests.rs:469` and `PermissionDenied` while executing fixture binaries. This is my environment override, not a code change. The gate is continuing through static builds; log: `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rebased.log`. I will rerun on this same head with short roots `/home/claude/j48t` and `/home/claude/j48a`, keeping socket paths short and fixtures on the worktree filesystem.
Author
Owner

Branch-owned gate finding on rebased head 8e981eede: scripts/verify fast strict workspace clippy reports -D dead-code for WebSocketState::new_with_hosts and new_with_public_origin (crates/infraternal-server/src/websocket.rs:204,223). Inspection confirms new_with_hosts is called only by a test helper, while new_with_public_origin is called only in the non-remote runtime branch; their current cfg attributes also include production/test combinations where neither has a caller. I will narrow those cfgs to their actual call sites, verify clippy in the feature combinations, then rerun the full gate on the new head.

Branch-owned gate finding on rebased head `8e981eede`: `scripts/verify fast` strict workspace clippy reports `-D dead-code` for `WebSocketState::new_with_hosts` and `new_with_public_origin` (`crates/infraternal-server/src/websocket.rs:204,223`). Inspection confirms `new_with_hosts` is called only by a test helper, while `new_with_public_origin` is called only in the non-remote runtime branch; their current cfg attributes also include production/test combinations where neither has a caller. I will narrow those cfgs to their actual call sites, verify clippy in the feature combinations, then rerun the full gate on the new head.
Author
Owner

Cfg fix committed and pushed as 18f8c1255. The exact workspace check on dev-02 passed through build-slot:

Finished `dev` profile [unoptimized + debuginfo] target(s) in 2m 45s

The first compute01 gate log is retained at /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rebased.log; I stopped that run after the confirmed clippy failure and the invalid tmpfs fixture-root failures, so it would not occupy a slot for a gate already known to fail. No J48 process remains from that run. I am syncing 18f8c1255 to both VM worktrees and will run the final checks with short same-filesystem roots under /home/claude.

Cfg fix committed and pushed as `18f8c1255`. The exact workspace check on dev-02 passed through `build-slot`: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 2m 45s ``` The first compute01 gate log is retained at `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-rebased.log`; I stopped that run after the confirmed clippy failure and the invalid tmpfs fixture-root failures, so it would not occupy a slot for a gate already known to fail. No J48 process remains from that run. I am syncing `18f8c1255` to both VM worktrees and will run the final checks with short same-filesystem roots under `/home/claude`.
Author
Owner

J48 crash-safety progress at pushed head 18f8c1255: final dev-02 feature-on/off suites, clippy in both modes, format, and locked DNS fuzz check all exit 0; the root namespace test passes and cleans up. The corrected compute01 scripts/verify fast is queued in build-slot at position 5, waiting 8 minutes so far; free disk is 42 GB. I removed only the stale target left by the earlier aborted run to get back above the slot floor. The queued final run has not started compiling yet; log path is /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-final.log.

J48 crash-safety progress at pushed head `18f8c1255`: final dev-02 feature-on/off suites, clippy in both modes, format, and locked DNS fuzz check all exit 0; the root namespace test passes and cleans up. The corrected compute01 `scripts/verify fast` is queued in `build-slot` at position 5, waiting 8 minutes so far; free disk is 42 GB. I removed only the stale target left by the earlier aborted run to get back above the slot floor. The queued final run has not started compiling yet; log path is `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-final.log`.
Author
Owner

J48 progress on pushed mesh-dns head 18f8c1255, rebased onto origin/dev 37bbda45554f6e603a6856fc23319e17d8a1b14f.

The feature-on and compile-out mesh suites, clippy in both feature modes, formatting, locked DNS fuzz check, and root namespace test passed on this head. The existing mesh-view hidden-hostname privacy test passes in both feature modes. The required compute01 scripts/verify fast is still running through workspace tests under build-slot; its log is /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-final.log. Shared disk is currently 7.6 GB free, so I am keeping the active run's single target and will wait for it to finish before the size comparison and cleanup. No source changes are pending.

J48 progress on pushed `mesh-dns` head `18f8c1255`, rebased onto `origin/dev` `37bbda45554f6e603a6856fc23319e17d8a1b14f`. The feature-on and compile-out mesh suites, clippy in both feature modes, formatting, locked DNS fuzz check, and root namespace test passed on this head. The existing mesh-view hidden-hostname privacy test passes in both feature modes. The required compute01 `scripts/verify fast` is still running through workspace tests under build-slot; its log is `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/verify-fast-final.log`. Shared disk is currently 7.6 GB free, so I am keeping the active run's single target and will wait for it to finish before the size comparison and cleanup. No source changes are pending.
Author
Owner

J48 finished on mesh-dns at 18f8c125557b11b333a12e324312096e3388c749, based on current origin/dev 37bbda45554f6e603a6856fc23319e17d8a1b14f. The branch is pushed and clean.

Implemented the owner’s naming and reachability decisions:

  • Each receiving node’s DNS snapshot includes only names of peers it can reach according to that same directional compiled PeerPolicy used for WireGuard. A denied peer gets NXDOMAIN and has no record in its snapshot. Grants, revocations and policy changes replace the accepted snapshot atomically.
  • DNS serves the real hostname. Placeholder-only names are omitted; no unnamed-<id> projection is used. Existing duplicate and collision behavior is unchanged.
  • The server’s configured public-origin hostname supplies the default suffix; .internal remains an alias, and a configured suffix override wins. public_origin now flows from runtime through WebSocket/plugin construction into snapshot compilation; suffix changes advance the snapshot revision.
  • Names remain mesh-only. Inventory and mesh-view APIs are unchanged. The existing a_non_owner_cannot_confirm_the_servers_hidden_hostname mesh-view privacy test passes with DNS enabled and disabled. Under the owner’s DNS decision, an allowed peer can resolve the real hostname; a denied peer cannot.
  • mesh-dns-spike stays default-off, with simple-dns behind it; no production library choice, TLS or canvas serving was added.

The feature-on tests cover denied NXDOMAIN/snapshot omission, grant and revocation, one-way node and tag grants, tag membership without a grant, a port-limited grant, real hostname answers, public-origin suffix derivation, .internal alias and explicit override. The compile-out mesh suite passes too. User/device and user-group grants are not expressible in today’s model (Endpoint::User is rejected, nodes lack user ownership, and tags identify nodes); that owner question is recorded here and cross-referenced on #173. No separate DNS policy was introduced.

Final scripts/verify fast on compute01 exited 0. Verbatim Rust summary:

+---------------------------+--------------+
| Rust gate                 | Result       |
+---------------------------+--------------+
| fmt                       | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| real-binary fixture paths | PASS         |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+

The gate’s static-build size lines are report-only under the amended D243 rule:

PT_INTERP: absent; size: 21448240 bytes; baseline 21443824; delta +4416 bytes (report only, D243 amended 2026-11-01)
PT_INTERP: absent; size: 19635592 bytes; baseline 19629240; delta +6352 bytes (report only, D243 amended 2026-11-01)

Fresh same-recipe comparison against current dev 37bbda455:

Target Current dev J48 head Delta
x86_64 21,448,272 bytes 21,448,240 bytes −32 bytes
aarch64 19,635,240 bytes 19,635,592 bytes +352 bytes

Verbatim TypeScript summary:

 39 pass
 0 fail
 59970 expect() calls
Ran 39 tests across 10 files. [9.37s]

The root namespace test via cua@infraternal-dev-02-std, sudo and build-slot passed:

test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 9 filtered out; finished in 0.21s

Feature-on/off mesh suites, both clippy modes, formatting and the locked DNS fuzz-target check exited 0; the namespace cleanup passed. Receipts and static binaries are under /home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/ on the respective VMs. The full fast log is verify-fast-final.log; the namespace receipt is root-namespace-final.log. All J48 target directories have been removed, and no J48 run remains active.

J48 finished on `mesh-dns` at `18f8c125557b11b333a12e324312096e3388c749`, based on current `origin/dev` `37bbda45554f6e603a6856fc23319e17d8a1b14f`. The branch is pushed and clean. Implemented the owner’s naming and reachability decisions: - Each receiving node’s DNS snapshot includes only names of peers it can reach according to that same directional compiled `PeerPolicy` used for WireGuard. A denied peer gets NXDOMAIN and has no record in its snapshot. Grants, revocations and policy changes replace the accepted snapshot atomically. - DNS serves the real hostname. Placeholder-only names are omitted; no `unnamed-<id>` projection is used. Existing duplicate and collision behavior is unchanged. - The server’s configured public-origin hostname supplies the default suffix; `.internal` remains an alias, and a configured suffix override wins. `public_origin` now flows from runtime through WebSocket/plugin construction into snapshot compilation; suffix changes advance the snapshot revision. - Names remain mesh-only. Inventory and mesh-view APIs are unchanged. The existing `a_non_owner_cannot_confirm_the_servers_hidden_hostname` mesh-view privacy test passes with DNS enabled and disabled. Under the owner’s DNS decision, an allowed peer can resolve the real hostname; a denied peer cannot. - `mesh-dns-spike` stays default-off, with `simple-dns` behind it; no production library choice, TLS or canvas serving was added. The feature-on tests cover denied NXDOMAIN/snapshot omission, grant and revocation, one-way node and tag grants, tag membership without a grant, a port-limited grant, real hostname answers, public-origin suffix derivation, `.internal` alias and explicit override. The compile-out mesh suite passes too. User/device and user-group grants are not expressible in today’s model (`Endpoint::User` is rejected, nodes lack user ownership, and tags identify nodes); that owner question is recorded here and cross-referenced on #173. No separate DNS policy was introduced. Final `scripts/verify fast` on compute01 exited `0`. Verbatim Rust summary: ```text +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | fmt | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | static builds | PASS | +---------------------------+--------------+ ``` The gate’s static-build size lines are report-only under the amended D243 rule: ```text PT_INTERP: absent; size: 21448240 bytes; baseline 21443824; delta +4416 bytes (report only, D243 amended 2026-11-01) PT_INTERP: absent; size: 19635592 bytes; baseline 19629240; delta +6352 bytes (report only, D243 amended 2026-11-01) ``` Fresh same-recipe comparison against current dev `37bbda455`: | Target | Current dev | J48 head | Delta | | --- | ---: | ---: | ---: | | x86_64 | 21,448,272 bytes | 21,448,240 bytes | −32 bytes | | aarch64 | 19,635,240 bytes | 19,635,592 bytes | +352 bytes | Verbatim TypeScript summary: ```text 39 pass 0 fail 59970 expect() calls Ran 39 tests across 10 files. [9.37s] ``` The root namespace test via `cua@infraternal-dev-02-std`, sudo and `build-slot` passed: ```text test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 9 filtered out; finished in 0.21s ``` Feature-on/off mesh suites, both clippy modes, formatting and the locked DNS fuzz-target check exited `0`; the namespace cleanup passed. Receipts and static binaries are under `/home/claude/.cache/codex-scratch/j48-mesh-dns/evidence/` on the respective VMs. The full fast log is `verify-fast-final.log`; the namespace receipt is `root-namespace-final.log`. All J48 target directories have been removed, and no J48 run remains active.
Author
Owner

J68 independent review round 1 starts: target mesh-dns at 18f8c125557b11b333a12e324312096e3388c749, base origin/dev at 37bbda45554f6e603a6856fc23319e17d8a1b14f. Read-only detached review worktree: /home/kayg/Developer/Worktrees/infraternal-j68.

Plan (45 minutes): read D165/D197/D316 and owner comments; trace per-receiver policy/name compilation, accepted-snapshot replacement and suffix updates; inspect hostile-packet parsing/fuzz coverage and default-off dependency/listener gating; prove any finding with a focused VM test if required, then post numbered P0–P3 findings or APPROVE. Tests only to prove findings; no repository code changes planned, gate not applicable.

J68 independent review round 1 starts: target `mesh-dns` at `18f8c125557b11b333a12e324312096e3388c749`, base `origin/dev` at `37bbda45554f6e603a6856fc23319e17d8a1b14f`. Read-only detached review worktree: `/home/kayg/Developer/Worktrees/infraternal-j68`. Plan (45 minutes): read D165/D197/D316 and owner comments; trace per-receiver policy/name compilation, accepted-snapshot replacement and suffix updates; inspect hostile-packet parsing/fuzz coverage and default-off dependency/listener gating; prove any finding with a focused VM test if required, then post numbered P0–P3 findings or APPROVE. Tests only to prove findings; no repository code changes planned, gate not applicable.
Author
Owner

Owner decisions (kayg, 2026-11-01):

  • DNS wire library: hickory-proto, not simple-dns. Slice 3 brings hickory-resolver, which already depends on hickory-proto, so the product carries one DNS library from the start. The mesh-dns slice 1 parser and encoder switch to hickory-proto as a short follow-up on the branch, before merge. R1's CNAME-chasing finding concerns hickory's high-level stub resolver in forwarding (slice 3), not the wire library.
  • User and group grants: agreed; see #173. Machines get an owner (the person who added them), and rules can name people and groups. Scheduled right after mesh SSH S1, which needs the same person identity.
Owner decisions (kayg, 2026-11-01): - **DNS wire library: hickory-proto, not simple-dns.** Slice 3 brings hickory-resolver, which already depends on hickory-proto, so the product carries one DNS library from the start. The `mesh-dns` slice 1 parser and encoder switch to hickory-proto as a short follow-up on the branch, before merge. R1's CNAME-chasing finding concerns hickory's high-level stub *resolver* in forwarding (slice 3), not the wire library. - **User and group grants:** agreed; see #173. Machines get an owner (the person who added them), and rules can name people and groups. Scheduled right after mesh SSH S1, which needs the same person identity.
Author
Owner

J68 scope update from orchestrator: drop check (4), parser robustness, because the owner chose hickory-proto and the replacement gets its own short review. Remaining scope: per-receiver policy visibility, atomic snapshots, suffix derivation/override and default-off/compile-out. The focused UDP disclosure reproducer remains relevant because it tests source authorization at the listener boundary, independently of packet-library choice; it is currently queued on dev-02 through build-slot.

J68 scope update from orchestrator: drop check (4), parser robustness, because the owner chose hickory-proto and the replacement gets its own short review. Remaining scope: per-receiver policy visibility, atomic snapshots, suffix derivation/override and default-off/compile-out. The focused UDP disclosure reproducer remains relevant because it tests source authorization at the listener boundary, independently of packet-library choice; it is currently queued on dev-02 through build-slot.
Author
Owner

J68 independent review round 1: REQUEST CHANGES

Reviewed mesh-dns at 18f8c125557b11b333a12e324312096e3388c749 against origin/dev at 37bbda45554f6e603a6856fc23319e17d8a1b14f. Parser robustness was excluded after the orchestrator's updated owner direction to replace the parser with hickory-proto.

  1. P1: querying another node's UDP resolver bypasses per-receiver name visibility. crates/plugins/mesh/src/dns.rs:304–316, especially :315: the listener receives the source address but selects records only from its host's snapshot, and sends that answer to every source. It never checks whose policy the requester has. The compiler correctly omits denied names from the requester's own snapshot, but that protection is bypassed by querying a reachable node with broader access. In particular, all mesh nodes can reach the server (configuration.rs:82), and the server accepts their incoming traffic (configuration.rs:89–90). If the server has a grant to target C while requester B does not, B can query @<server-mesh-ip>:1053 C.internal and obtain C's address. The same issue preserves visibility after B's grant is revoked while another reachable resolver retains its grant. This violates owner comment 72296's rule that only peers allowed to reach a target learn its name. Restrict the resolver to node-local clients, or authorize answers using the actual requester's directional policy; permission to contact the resolver host is insufficient.

    Evidence: added review_j68_remote_resolver_must_not_disclose_a_name_denied_to_the_asker at crates/plugins/mesh/tests/dns.rs:429 on local review branch review-j68, commit c38444107 (deliberately failing regression, no implementation changes). It uses the real coordinator/compiler with three nodes and default-deny, grants only server 1 → target 3, asserts requester 2 → target 3 is denied and requester 2 → server 1 is permitted in the compiled policies, verifies the requester's local NXDOMAIN, then exercises the production resolver over UDP using separate loopback source/listener addresses. This isolates the listener authorization boundary without a kernel mesh fixture; tunnel reachability is established by the compiled policy assertions.

    Command on compute01 through build-slot, nice -n10, four build jobs:

    cargo test -p infraternal-plugin-mesh --features mesh-dns-spike --test dns review_j68_remote_resolver -- --nocapture
    

    Verbatim result:

    asker 2 -> target 3: denied; asker 2 -> server 1 DNS: permitted; remote response: NoError, 1 answers
    assertion `left == right` failed: remote queries must not inherit the resolver host's broader policy
      left: NoError
     right: NameError
    test review_j68_remote_resolver_must_not_disclose_a_name_denied_to_the_asker ... FAILED
    test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 11 filtered out; finished in 0.00s
    

    Exit 101. Full receipt: compute01 /home/claude/.cache/codex-scratch/j68/repro.log; control-host copy /home/kayg/.local/state/infraternal-orchestrator/j68/repro.log.

No additional findings from the accepted-snapshot/chunk replacement, public-origin suffix/custom override/.internal alias, or default-off/compile-out source review. Size and the recorded user/group policy question were not treated as findings. No parser findings are included.

Review complete. scripts/verify fast: not run, review-only job; only the focused test needed to prove the finding was run. The dev-02 queued attempt was stopped before compilation (exit 143); the compute01 test exited 101, its listener joined on drop, and its generated target directory was removed. No J68 run remains pending. Review branch was not pushed; reviewed implementation head remains 18f8c1255, local test head c38444107.

## J68 independent review round 1: REQUEST CHANGES Reviewed `mesh-dns` at `18f8c125557b11b333a12e324312096e3388c749` against `origin/dev` at `37bbda45554f6e603a6856fc23319e17d8a1b14f`. Parser robustness was excluded after the orchestrator's updated owner direction to replace the parser with hickory-proto. 1. **P1: querying another node's UDP resolver bypasses per-receiver name visibility.** `crates/plugins/mesh/src/dns.rs:304–316`, especially `:315`: the listener receives the source address but selects records only from its host's snapshot, and sends that answer to every source. It never checks whose policy the requester has. The compiler correctly omits denied names from the requester's own snapshot, but that protection is bypassed by querying a reachable node with broader access. In particular, all mesh nodes can reach the server (`configuration.rs:82`), and the server accepts their incoming traffic (`configuration.rs:89–90`). If the server has a grant to target C while requester B does not, B can query `@<server-mesh-ip>:1053 C.internal` and obtain C's address. The same issue preserves visibility after B's grant is revoked while another reachable resolver retains its grant. This violates owner comment 72296's rule that only peers allowed to reach a target learn its name. Restrict the resolver to node-local clients, or authorize answers using the actual requester's directional policy; permission to contact the resolver host is insufficient. **Evidence:** added `review_j68_remote_resolver_must_not_disclose_a_name_denied_to_the_asker` at `crates/plugins/mesh/tests/dns.rs:429` on local review branch `review-j68`, commit `c38444107` (deliberately failing regression, no implementation changes). It uses the real coordinator/compiler with three nodes and default-deny, grants only server 1 → target 3, asserts requester 2 → target 3 is denied and requester 2 → server 1 is permitted in the compiled policies, verifies the requester's local NXDOMAIN, then exercises the production resolver over UDP using separate loopback source/listener addresses. This isolates the listener authorization boundary without a kernel mesh fixture; tunnel reachability is established by the compiled policy assertions. Command on compute01 through `build-slot`, `nice -n10`, four build jobs: ```text cargo test -p infraternal-plugin-mesh --features mesh-dns-spike --test dns review_j68_remote_resolver -- --nocapture ``` Verbatim result: ```text asker 2 -> target 3: denied; asker 2 -> server 1 DNS: permitted; remote response: NoError, 1 answers assertion `left == right` failed: remote queries must not inherit the resolver host's broader policy left: NoError right: NameError test review_j68_remote_resolver_must_not_disclose_a_name_denied_to_the_asker ... FAILED test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 11 filtered out; finished in 0.00s ``` Exit `101`. Full receipt: compute01 `/home/claude/.cache/codex-scratch/j68/repro.log`; control-host copy `/home/kayg/.local/state/infraternal-orchestrator/j68/repro.log`. No additional findings from the accepted-snapshot/chunk replacement, public-origin suffix/custom override/.internal alias, or default-off/compile-out source review. Size and the recorded user/group policy question were not treated as findings. No parser findings are included. Review complete. `scripts/verify fast`: not run, review-only job; only the focused test needed to prove the finding was run. The dev-02 queued attempt was stopped before compilation (exit 143); the compute01 test exited 101, its listener joined on drop, and its generated target directory was removed. No J68 run remains pending. Review branch was not pushed; reviewed implementation head remains `18f8c1255`, local test head `c38444107`.
Author
Owner

J68b independent review round 1 starts: mesh-dns head 18f8c125557b11b333a12e324312096e3388c749 versus origin/dev 37bbda45554f6e603a6856fc23319e17d8a1b14f. Isolated review branch review-j68b, worktree /home/kayg/Developer/Worktrees/infraternal-j68b. Plan (30 minutes): trace directional PeerPolicy/name visibility, atomic snapshot acceptance/replacement, suffix derivation/override and default-off/compile-out; confirm any suspected finding with focused VM tests only; post verdict. Packet parsing, size and #173 grants excluded. No production code changes planned; gate not applicable.

J68b independent review round 1 starts: mesh-dns head `18f8c125557b11b333a12e324312096e3388c749` versus origin/dev `37bbda45554f6e603a6856fc23319e17d8a1b14f`. Isolated review branch `review-j68b`, worktree `/home/kayg/Developer/Worktrees/infraternal-j68b`. Plan (30 minutes): trace directional PeerPolicy/name visibility, atomic snapshot acceptance/replacement, suffix derivation/override and default-off/compile-out; confirm any suspected finding with focused VM tests only; post verdict. Packet parsing, size and #173 grants excluded. No production code changes planned; gate not applicable.
Author
Owner

J68b independent code review, round 1: REQUEST CHANGES

Target: mesh-dns at 18f8c125557b11b333a12e324312096e3388c749 versus origin/dev at 37bbda45554f6e603a6856fc23319e17d8a1b14f. Read BRIEF-COMMON, PROCESS, ARCHITECTURE, D165/D197/D316 and owner comments 72296, 72627, 73114 plus J48 summary 73105. Packet parser replacement, binary size and #173 user/group grants excluded.

  1. P1: a reachable node's resolver discloses names denied to the requester. crates/plugins/mesh/src/dns.rs:315 (source received at :304; reply sent at :316). The worker always answers from its own node's records, without checking the requester's identity or reachability. The compiler filters each local snapshot correctly, but requester B can bypass its local NXDOMAIN by querying resolver A when A may reach target C and B may not. The server is an immediately reachable resolver: configuration.rs:82–90 permits every node's outbound server traffic and the server's inbound traffic. Granting server → C therefore lets B query C through the server's resolver despite B → C being denied. Revoking B's grant does not remove this alternate disclosure path. Restrict clients to node-local queries or enforce the actual asker's policy, rather than inheriting the resolver host's policy.

    Evidence: independently traced the listener and compiled server policies; inspected J68's committed reproducer c38444107 and its completed compute01 receipt, already posted in comment 73121. Its real UDP reply was NoError, 1 answers while the requester's compiled policy denied the target and its local response was NXDOMAIN. J68b did not rerun that established reproducer. This confirms the same P1, not a second independent issue.

  2. P2: explicitly selecting .internal cannot override the public-origin suffix. crates/plugins/mesh/src/configuration.rs:63–66. The compiler interprets every persisted "internal" value as an unset default, so it cannot distinguish an explicit override from no override. With origin https://ssh.kayg.org, setting the suffix to temporary.example and then explicitly back to internal still compiles dns_suffix = "ssh.kayg.org". This contradicts the requested explicit-override precedence and prevents disabling the origin-derived zone by choosing .internal. Represent an unset default separately from a concrete suffix.

    Evidence: failing regression review_j68b_explicit_internal_suffix_overrides_origin, crates/plugins/mesh/tests/configuration.rs:523, committed as dfb5abc7f. It uses the real coordinator's set_suffix and production compiler.

  3. P2: a valid custom suffix can shadow an existing .internal hostname alias. crates/plugins/mesh/src/dns.rs:205–207 (suffixes ordered longest first at :52). For local hostname laptop and valid custom suffix laptop.internal, laptop.laptop.internal resolves, but laptop.internal matches the custom zone apex first, produces an empty label and returns NXDOMAIN instead of the local address. The unconditional .internal alias therefore disappears for a reachable name. Preserve concrete hostname aliases when zones overlap, or reject this configuration explicitly instead of accepting it and losing an alias.

    Evidence: failing regression review_j68b_nested_suffix_preserves_internal_hostname_alias, crates/plugins/mesh/tests/dns.rs:429, committed as 9c74e9746. This exercises normal A queries and production zone selection, independently of packet-parser robustness.

Focused confirmation on compute01 through build-slot, nice -n10, Nix, -j4:

cargo test -j4 -p infraternal-plugin-mesh --features mesh-dns-spike --test dns --test configuration --no-fail-fast review_j68b -- --nocapture

Verbatim relevant output:

explicit internal setting compiled as ssh.kayg.org
assertion `left == right` failed: explicit suffix overrides the origin-derived default
  left: "ssh.kayg.org"
 right: "internal"
test review_j68b_explicit_internal_suffix_overrides_origin ... FAILED
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 17 filtered out; finished in 0.00s

laptop.laptop.internal: NoError, 1 answers
laptop.internal: NameError, 0 answers
assertion `left == right` failed: the configured suffix must preserve every .internal alias
  left: NameError
 right: NoError
test review_j68b_nested_suffix_preserves_internal_hostname_alias ... FAILED
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 11 filtered out; finished in 0.00s
REPRO_EXIT=101

No further findings from complete-chunk staging/record replacement or default-off/compile-out source inspection. No implementation code changed. scripts/verify fast: not run, review-only scope; tests were run only to confirm findings. The two review commits intentionally retain failing regressions.

Review branch review-j68b, head 9c74e97466953e5ee3853d4cfddfd5006621c450, based on reviewed implementation head 18f8c1255; branch not pushed. Receipts: compute01 /home/claude/.cache/codex-scratch/j68b/repro-final.log, control host /home/kayg/.local/state/infraternal-orchestrator/j68b/repro-final.log. All J68b commands have completed; its VM target directory was removed. No pending J68b run.

## J68b independent code review, round 1: REQUEST CHANGES Target: `mesh-dns` at `18f8c125557b11b333a12e324312096e3388c749` versus `origin/dev` at `37bbda45554f6e603a6856fc23319e17d8a1b14f`. Read BRIEF-COMMON, PROCESS, ARCHITECTURE, D165/D197/D316 and owner comments 72296, 72627, 73114 plus J48 summary 73105. Packet parser replacement, binary size and #173 user/group grants excluded. 1. **P1: a reachable node's resolver discloses names denied to the requester.** `crates/plugins/mesh/src/dns.rs:315` (source received at :304; reply sent at :316). The worker always answers from its own node's records, without checking the requester's identity or reachability. The compiler filters each local snapshot correctly, but requester B can bypass its local NXDOMAIN by querying resolver A when A may reach target C and B may not. The server is an immediately reachable resolver: `configuration.rs:82–90` permits every node's outbound server traffic and the server's inbound traffic. Granting server → C therefore lets B query C through the server's resolver despite B → C being denied. Revoking B's grant does not remove this alternate disclosure path. Restrict clients to node-local queries or enforce the actual asker's policy, rather than inheriting the resolver host's policy. Evidence: independently traced the listener and compiled server policies; inspected J68's committed reproducer `c38444107` and its completed compute01 receipt, already posted in comment 73121. Its real UDP reply was `NoError, 1 answers` while the requester's compiled policy denied the target and its local response was NXDOMAIN. J68b did not rerun that established reproducer. This confirms the same P1, not a second independent issue. 2. **P2: explicitly selecting `.internal` cannot override the public-origin suffix.** `crates/plugins/mesh/src/configuration.rs:63–66`. The compiler interprets every persisted `"internal"` value as an unset default, so it cannot distinguish an explicit override from no override. With origin `https://ssh.kayg.org`, setting the suffix to `temporary.example` and then explicitly back to `internal` still compiles `dns_suffix = "ssh.kayg.org"`. This contradicts the requested explicit-override precedence and prevents disabling the origin-derived zone by choosing `.internal`. Represent an unset default separately from a concrete suffix. Evidence: failing regression `review_j68b_explicit_internal_suffix_overrides_origin`, `crates/plugins/mesh/tests/configuration.rs:523`, committed as `dfb5abc7f`. It uses the real coordinator's `set_suffix` and production compiler. 3. **P2: a valid custom suffix can shadow an existing `.internal` hostname alias.** `crates/plugins/mesh/src/dns.rs:205–207` (suffixes ordered longest first at :52). For local hostname `laptop` and valid custom suffix `laptop.internal`, `laptop.laptop.internal` resolves, but `laptop.internal` matches the custom zone apex first, produces an empty label and returns NXDOMAIN instead of the local address. The unconditional `.internal` alias therefore disappears for a reachable name. Preserve concrete hostname aliases when zones overlap, or reject this configuration explicitly instead of accepting it and losing an alias. Evidence: failing regression `review_j68b_nested_suffix_preserves_internal_hostname_alias`, `crates/plugins/mesh/tests/dns.rs:429`, committed as `9c74e9746`. This exercises normal A queries and production zone selection, independently of packet-parser robustness. Focused confirmation on compute01 through `build-slot`, `nice -n10`, Nix, `-j4`: ```text cargo test -j4 -p infraternal-plugin-mesh --features mesh-dns-spike --test dns --test configuration --no-fail-fast review_j68b -- --nocapture ``` Verbatim relevant output: ```text explicit internal setting compiled as ssh.kayg.org assertion `left == right` failed: explicit suffix overrides the origin-derived default left: "ssh.kayg.org" right: "internal" test review_j68b_explicit_internal_suffix_overrides_origin ... FAILED test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 17 filtered out; finished in 0.00s laptop.laptop.internal: NoError, 1 answers laptop.internal: NameError, 0 answers assertion `left == right` failed: the configured suffix must preserve every .internal alias left: NameError right: NoError test review_j68b_nested_suffix_preserves_internal_hostname_alias ... FAILED test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 11 filtered out; finished in 0.00s REPRO_EXIT=101 ``` No further findings from complete-chunk staging/record replacement or default-off/compile-out source inspection. No implementation code changed. `scripts/verify fast`: not run, review-only scope; tests were run only to confirm findings. The two review commits intentionally retain failing regressions. Review branch `review-j68b`, head `9c74e97466953e5ee3853d4cfddfd5006621c450`, based on reviewed implementation head `18f8c1255`; branch not pushed. Receipts: compute01 `/home/claude/.cache/codex-scratch/j68b/repro-final.log`, control host `/home/kayg/.local/state/infraternal-orchestrator/j68b/repro-final.log`. All J68b commands have completed; its VM target directory was removed. No pending J68b run.
Author
Owner

J48 fix-round plan and estimate. I’m starting from pushed mesh-dns head 18f8c125557b11b333a12e324312096e3388c749; I will rebase onto current origin/dev if it has moved.

  1. Read J68b’s #73130 findings and locate/reproduce its committed regressions on dev-02 through build-slot (about 30 minutes).
  2. Fix resolver source authorization using the asker's mesh address and directional policy; run the focused regression and commit this finding alone (about 45 minutes).
  3. Fix explicit .internal override semantics and commit that finding alone (about 30 minutes).
  4. Preserve the concrete .internal hostname alias when a custom zone overlaps it and commit that finding alone (about 30 minutes).
  5. Replace the slice-1 packet parser/encoder with hickory-proto, preserve default-off behavior and the fuzz target, run focused tests, and commit the library swap alone (about 60 minutes).
  6. Run feature-on/off mesh suites, clippy, formatting and fuzz-target checks on dev-02; run the final scripts/verify fast and static-size comparison on compute01; clean targets, push and post the final evidence (about 45 minutes plus build-slot waits).

Estimate: roughly 4 hours of work, excluding shared build-slot wait time. Each finding and the library swap will be a separate atomic commit.

J48 fix-round plan and estimate. I’m starting from pushed `mesh-dns` head `18f8c125557b11b333a12e324312096e3388c749`; I will rebase onto current `origin/dev` if it has moved. 1. Read J68b’s #73130 findings and locate/reproduce its committed regressions on dev-02 through `build-slot` (about 30 minutes). 2. Fix resolver source authorization using the asker's mesh address and directional policy; run the focused regression and commit this finding alone (about 45 minutes). 3. Fix explicit `.internal` override semantics and commit that finding alone (about 30 minutes). 4. Preserve the concrete `.internal` hostname alias when a custom zone overlaps it and commit that finding alone (about 30 minutes). 5. Replace the slice-1 packet parser/encoder with `hickory-proto`, preserve default-off behavior and the fuzz target, run focused tests, and commit the library swap alone (about 60 minutes). 6. Run feature-on/off mesh suites, clippy, formatting and fuzz-target checks on dev-02; run the final `scripts/verify fast` and static-size comparison on compute01; clean targets, push and post the final evidence (about 45 minutes plus build-slot waits). Estimate: roughly 4 hours of work, excluding shared build-slot wait time. Each finding and the library swap will be a separate atomic commit.
Author
Owner

J48 fix-round finding before implementation: the P1 requires more than checking the incoming UDP source. Resolver::bind builds one Records from the local node's Config; Config.peer_names is explicitly that receiver's filtered view, and node.rs owns only that accepted config. A listener can map a mesh source address to a peer ID from its local peer table, but it has no filtered DNS snapshot for that peer. Answering from the listener's Records repeats the disclosure; distributing every peer's view to every resolver would itself disclose names beyond that resolver node's grants.

Evidence: crates/plugins/mesh/src/dns.rs Records::from_config / Resolver::bind; crates/plugins/mesh/src/dataplane.rs Config.peer_names; crates/plugins/mesh/src/node.rs resolver construction and accepted self.config. J68b's real-UDP reproducer is review-j68 commit c38444107200c5e5a08f5e0204ff0372a23a81bf and its red receipt is recorded above; it confirms a remote requester currently gets the resolver host's NoError answer.

To meet the requested per-asker snapshot semantics for cross-node queries, this fix needs a selective coordinator-to-resolver snapshot lookup/delivery path. That is extra architecture beyond the four local changes in the posted plan; estimated additional work is 2–3 hours. I have not changed production code while this is unresolved.

J48 fix-round finding before implementation: the P1 requires more than checking the incoming UDP source. `Resolver::bind` builds one `Records` from the local node's `Config`; `Config.peer_names` is explicitly that receiver's filtered view, and `node.rs` owns only that accepted config. A listener can map a mesh source address to a peer ID from its local peer table, but it has no filtered DNS snapshot for that peer. Answering from the listener's `Records` repeats the disclosure; distributing every peer's view to every resolver would itself disclose names beyond that resolver node's grants. Evidence: `crates/plugins/mesh/src/dns.rs` `Records::from_config` / `Resolver::bind`; `crates/plugins/mesh/src/dataplane.rs` `Config.peer_names`; `crates/plugins/mesh/src/node.rs` resolver construction and accepted `self.config`. J68b's real-UDP reproducer is `review-j68` commit `c38444107200c5e5a08f5e0204ff0372a23a81bf` and its red receipt is recorded above; it confirms a remote requester currently gets the resolver host's `NoError` answer. To meet the requested per-asker snapshot semantics for cross-node queries, this fix needs a selective coordinator-to-resolver snapshot lookup/delivery path. That is extra architecture beyond the four local changes in the posted plan; estimated additional work is 2–3 hours. I have not changed production code while this is unresolved.
Author
Owner

Owner clarification for the P1: slice 1 is local-only. Each node's own resolver serves only that node using its own filtered snapshot. A request from any other source receives REFUSED with no names. Remote DNS use (including devices without an infraternal agent) is a later slice requiring per-asker snapshots. I will document this limit in the DNS module and cover it with the J68 regression test.

Owner clarification for the P1: slice 1 is local-only. Each node's own resolver serves only that node using its own filtered snapshot. A request from any other source receives `REFUSED` with no names. Remote DNS use (including devices without an infraternal agent) is a later slice requiring per-asker snapshots. I will document this limit in the DNS module and cover it with the J68 regression test.
Author
Owner

P1 red/green evidence on dev-02 through build-slot: the J68 UDP reproducer first returned NoError, 1 answers against a requester whose local snapshot denied the target. Under the owner’s local-only rule, the regression now sends from a second configured mesh address and expects REFUSED; the resolver returns Refused, 0 answers, and a query sourced from the resolver node's own mesh address still receives its local answer.

Final focused DNS suite: test result: ok. 11 passed; 0 failed; 1 ignored. Feature-on clippy (-D warnings) and cargo fmt --all -- --check pass. The DNS module documents that cross-node resolver service is deferred until a per-asker snapshot path exists.

P1 red/green evidence on dev-02 through `build-slot`: the J68 UDP reproducer first returned `NoError, 1 answers` against a requester whose local snapshot denied the target. Under the owner’s local-only rule, the regression now sends from a second configured mesh address and expects `REFUSED`; the resolver returns `Refused, 0 answers`, and a query sourced from the resolver node's own mesh address still receives its local answer. Final focused DNS suite: `test result: ok. 11 passed; 0 failed; 1 ignored`. Feature-on clippy (`-D warnings`) and `cargo fmt --all -- --check` pass. The DNS module documents that cross-node resolver service is deferred until a per-asker snapshot path exists.
Author
Owner

J48 progress: rebased mesh-dns onto current origin/dev at a26615c50e76ed86f36d25aa094283cf840d5320 with no conflicts and pushed rewritten branch head 240f79612. The P1 scope is now local-only per owner direction: this node resolves only itself and remote sources receive REFUSED; J68 regression is green on dev-02. Next: run the committed .internal override and suffix-alias regressions red, then fix each in its own commit. I left the existing dirty VM path untouched and will use a fresh J48 VM worktree.

J48 progress: rebased `mesh-dns` onto current `origin/dev` at `a26615c50e76ed86f36d25aa094283cf840d5320` with no conflicts and pushed rewritten branch head `240f79612`. The P1 scope is now local-only per owner direction: this node resolves only itself and remote sources receive REFUSED; J68 regression is green on dev-02. Next: run the committed `.internal` override and suffix-alias regressions red, then fix each in its own commit. I left the existing dirty VM path untouched and will use a fresh J48 VM worktree.
Author
Owner

J48 P2 .internal override finding, RED on dev-02 through build-slot: the reviewer regression printed explicit internal setting compiled as ssh.kayg.org, then failed left: "ssh.kayg.org", right: "internal" (0 passed, 1 failed). This confirms origin-derived defaults currently override an explicit return to .internal. I’m adding persisted override state while preserving legacy non-internal values.

J48 P2 `.internal` override finding, RED on dev-02 through build-slot: the reviewer regression printed `explicit internal setting compiled as ssh.kayg.org`, then failed `left: "ssh.kayg.org"`, `right: "internal"` (0 passed, 1 failed). This confirms origin-derived defaults currently override an explicit return to `.internal`. I’m adding persisted override state while preserving legacy non-internal values.
Author
Owner

J48 P2 .internal override GREEN on dev-02 through build-slot: cargo test --locked -p infraternal-plugin-mesh --features mesh-dns-spike --test configuration -j3 completed with test result: ok. 18 passed; 0 failed; 0 ignored. The persisted explicitness bit preserves a selected internal suffix while absent legacy fields default to unset; older non-internal suffix values remain overrides.

J48 P2 `.internal` override GREEN on dev-02 through build-slot: `cargo test --locked -p infraternal-plugin-mesh --features mesh-dns-spike --test configuration -j3` completed with `test result: ok. 18 passed; 0 failed; 0 ignored`. The persisted explicitness bit preserves a selected `internal` suffix while absent legacy fields default to unset; older non-internal suffix values remain overrides.
Author
Owner

J48 P2 nested-suffix finding, RED on dev-02 through build-slot: with dns_suffix = "laptop.internal", the reviewer test returned laptop.laptop.internal: NoError, 1 answers but laptop.internal: NameError, 0 answers (0 passed, 1 failed). The longer configured suffix matched the alias name as its zone apex before .internal could match the hostname label.

J48 P2 nested-suffix finding, RED on dev-02 through build-slot: with `dns_suffix = "laptop.internal"`, the reviewer test returned `laptop.laptop.internal: NoError, 1 answers` but `laptop.internal: NameError, 0 answers` (0 passed, 1 failed). The longer configured suffix matched the alias name as its zone apex before `.internal` could match the hostname label.
Author
Owner

J48 alias regression GREEN on dev-02 through build-slot: review_j68b_nested_suffix_preserves_internal_hostname_alias now returned NoError, 1 answers for both laptop.laptop.internal and laptop.internal (1 passed, 0 failed). Zone selection now prefers a concrete hostname label over a longer suffix apex; I am running the full DNS target before committing this finding.

J48 alias regression GREEN on dev-02 through build-slot: `review_j68b_nested_suffix_preserves_internal_hostname_alias` now returned `NoError, 1 answers` for both `laptop.laptop.internal` and `laptop.internal` (1 passed, 0 failed). Zone selection now prefers a concrete hostname label over a longer suffix apex; I am running the full DNS target before committing this finding.
Author
Owner

J48 nested-suffix alias fix committed and pushed as adb88fd33. The reviewer regression was RED (laptop.internal NXDOMAIN) then GREEN (both names resolve, 1 passed). The queued full DNS target was canceled while waiting for shared slots; it will run with the complete feature-on validation after the parser migration.

J48 nested-suffix alias fix committed and pushed as `adb88fd33`. The reviewer regression was RED (`laptop.internal` NXDOMAIN) then GREEN (both names resolve, 1 passed). The queued full DNS target was canceled while waiting for shared slots; it will run with the complete feature-on validation after the parser migration.
Author
Owner

J48 resume: branch mesh-dns, based on origin/dev at a26615c50e76ed86f36d25aa094283cf840d5320, current pushed head adb88fd335c097a37bd981f2c5c8b2486942a6bc.

P1 scope confirmation: slice 1 is local-only. Each resolver answers only requests sourced from its own mesh address; other sources receive REFUSED with no records. I am not adding the selective coordinator-to-resolver snapshot path in this slice. Remote DNS service remains a later slice requiring the asker's own filtered snapshot. The module documentation and J68 UDP regression cover this limitation; the regression's RED/GREEN evidence is in the earlier progress comment.

Remaining plan (about 90 minutes of work, excluding build-slot waits): finish the Hickory wire parser/encoder migration and its tests/fuzz target; update lockfiles; run feature-on/off, clippy, formatting, fuzz compile and root namespace checks on dev-02; commit and push the library swap; compare static sizes and run final scripts/verify fast on compute01; remove J48 targets and post the gate output verbatim.

J48 resume: branch `mesh-dns`, based on `origin/dev` at `a26615c50e76ed86f36d25aa094283cf840d5320`, current pushed head `adb88fd335c097a37bd981f2c5c8b2486942a6bc`. P1 scope confirmation: slice 1 is local-only. Each resolver answers only requests sourced from its own mesh address; other sources receive `REFUSED` with no records. I am not adding the selective coordinator-to-resolver snapshot path in this slice. Remote DNS service remains a later slice requiring the asker's own filtered snapshot. The module documentation and J68 UDP regression cover this limitation; the regression's RED/GREEN evidence is in the earlier progress comment. Remaining plan (about 90 minutes of work, excluding build-slot waits): finish the Hickory wire parser/encoder migration and its tests/fuzz target; update lockfiles; run feature-on/off, clippy, formatting, fuzz compile and root namespace checks on dev-02; commit and push the library swap; compare static sizes and run final `scripts/verify fast` on compute01; remove J48 targets and post the gate output verbatim.
Author
Owner

J48 fix round starting. Branch mesh-dns, rebased base a26615c50e76ed86f36d25aa094283cf840d5320 (origin/dev).

Plan (about 90 minutes of work, excluding build-slot waits): (1) verify J68b's committed regressions and existing fixes; (2) retain the owner-approved slice-1 local-only resolver rule: answer only queries sourced from this node's mesh address, using its own accepted grant-filtered snapshot; return REFUSED with zero records to other sources; defer per-asker snapshot distribution; (3) finish the Hickory parser/encoder swap in the resolver, tests and fuzz target; (4) run feature-on and compile-out tests and scripts/verify fast on dev-02, then the final gate on compute01; (5) measure/report the static size delta, commit and push atomically, and post evidence.

J48 fix round starting. Branch `mesh-dns`, rebased base `a26615c50e76ed86f36d25aa094283cf840d5320` (`origin/dev`). Plan (about 90 minutes of work, excluding build-slot waits): (1) verify J68b's committed regressions and existing fixes; (2) retain the owner-approved slice-1 local-only resolver rule: answer only queries sourced from this node's mesh address, using its own accepted grant-filtered snapshot; return REFUSED with zero records to other sources; defer per-asker snapshot distribution; (3) finish the Hickory parser/encoder swap in the resolver, tests and fuzz target; (4) run feature-on and compile-out tests and `scripts/verify fast` on dev-02, then the final gate on compute01; (5) measure/report the static size delta, commit and push atomically, and post evidence.
Author
Owner

J48 progress: confirmed the owner-approved local-only resolver path and J68 regression; the resolver serves its accepted grant-filtered snapshot only to requests sourced from its own mesh address, and refuses other sources without records. The Hickory migration also updates the policy/naming test decoders. I restored the fuzz lock to the branch version so the queued cargo check can add only the replacement dependency graph instead of upgrading unrelated packages.

Validation job is queued on dev-02 at build-slot position 5 (status at 17:51 UTC); no compiler has started. Log: /home/claude/.cache/codex-scratch/j48/validate-slice.log. The job uses /home/claude/worktrees/j48-mesh-dns-r2/target as its single target directory. Next: feature-on/off tests, clippy and formatting, then the root namespace case and final compute01 gate.

J48 progress: confirmed the owner-approved local-only resolver path and J68 regression; the resolver serves its accepted grant-filtered snapshot only to requests sourced from its own mesh address, and refuses other sources without records. The Hickory migration also updates the policy/naming test decoders. I restored the fuzz lock to the branch version so the queued `cargo check` can add only the replacement dependency graph instead of upgrading unrelated packages. Validation job is queued on dev-02 at build-slot position 5 (status at 17:51 UTC); no compiler has started. Log: `/home/claude/.cache/codex-scratch/j48/validate-slice.log`. The job uses `/home/claude/worktrees/j48-mesh-dns-r2/target` as its single target directory. Next: feature-on/off tests, clippy and formatting, then the root namespace case and final compute01 gate.
Author
Owner

J48 validation progress: the first full dev-02 slice run passed fuzz cargo check, the feature-on dns, configuration, and store targets (38 passed, 1 ignored), and --no-default-features tests (5 passed). Feature-on clippy then reported too_many_lines at 101/100 in the J68 security regression; the Hickory decoder expansion pushed this cohesive test one line past the lint. I added a test-local allowance with a comment explaining why the policy setup and both sides of the UDP boundary stay together. The full validation rerun is queued on dev-02; its final status will be recorded before commit.

J48 validation progress: the first full dev-02 slice run passed fuzz `cargo check`, the feature-on `dns`, `configuration`, and `store` targets (38 passed, 1 ignored), and `--no-default-features` tests (5 passed). Feature-on clippy then reported `too_many_lines` at 101/100 in the J68 security regression; the Hickory decoder expansion pushed this cohesive test one line past the lint. I added a test-local allowance with a comment explaining why the policy setup and both sides of the UDP boundary stay together. The full validation rerun is queued on dev-02; its final status will be recorded before commit.
Author
Owner

J48 crash-safety snapshot pushed: a7ff9cfdf (WIP: use hickory-proto for mesh DNS wire handling (#266)). This is the single commit for the wire-library swap; it updates resolver parsing/encoding, all policy/naming/DNS reply decoders, and the fuzz dependency/lock while keeping mesh-dns-spike default-off. The feature-on and compile-out suites passed before the final test-local clippy allowance; the complete rerun, including fuzz check and clippy, is still queued on dev-02. Log: /home/claude/.cache/codex-scratch/j48/validate-slice.log. Next: wait for its explicit exit status, run the ignored root namespace test via CUA/sudo/build-slot, then run the final compute01 gate and report size.

J48 crash-safety snapshot pushed: `a7ff9cfdf` (`WIP: use hickory-proto for mesh DNS wire handling (#266)`). This is the single commit for the wire-library swap; it updates resolver parsing/encoding, all policy/naming/DNS reply decoders, and the fuzz dependency/lock while keeping `mesh-dns-spike` default-off. The feature-on and compile-out suites passed before the final test-local clippy allowance; the complete rerun, including fuzz check and clippy, is still queued on dev-02. Log: `/home/claude/.cache/codex-scratch/j48/validate-slice.log`. Next: wait for its explicit exit status, run the ignored root namespace test via CUA/sudo/build-slot, then run the final compute01 gate and report size.
Author
Owner

J48 validation progress: feature-on/off mesh tests, Hickory fuzz-target compile check, clippy (-D warnings) and fmt passed on dev-02 (VALIDATION_EXIT=0; log /home/claude/.cache/codex-scratch/j48/validate-slice.log). The root namespace test plus the existing hidden-hostname mesh-view privacy regression are queued first through dev-02 build-slot via CUA/sudo (log /home/claude/.cache/codex-scratch/j48/root-namespace.log). The final scripts/verify fast is running on compute01 from pushed head a7ff9cfdf (log /home/claude/.cache/codex-scratch/j48/final-gate.log). User/group endpoints are not expressible in current policy; #173 records the owner-approved follow-up after Mesh SSH S1. No mesh-view API output change is included here.

J48 validation progress: feature-on/off mesh tests, Hickory fuzz-target compile check, clippy (-D warnings) and fmt passed on dev-02 (VALIDATION_EXIT=0; log /home/claude/.cache/codex-scratch/j48/validate-slice.log). The root namespace test plus the existing hidden-hostname mesh-view privacy regression are queued first through dev-02 build-slot via CUA/sudo (log /home/claude/.cache/codex-scratch/j48/root-namespace.log). The final scripts/verify fast is running on compute01 from pushed head a7ff9cfdf (log /home/claude/.cache/codex-scratch/j48/final-gate.log). User/group endpoints are not expressible in current policy; #173 records the owner-approved follow-up after Mesh SSH S1. No mesh-view API output change is included here.
Author
Owner

J48 rebase update: origin/dev advanced from a26615c50 to afbe1c5fe (#296) while validation was queued. I rebased the 12 J48 commits without conflicts and force-pushed the own branch to dd59582e8, now based on afbe1c5fe. The earlier compute01 run is on the old base and will not be reported as final. Fresh feature-on/off, fuzz, clippy, fmt, and hidden-hostname privacy checks are running/queued on dev-02; the root namespace check and a fresh compute01 scripts/verify fast remain next. Current dev-02 validation log: /home/claude/.cache/codex-scratch/j48/validate-slice.log.

J48 rebase update: origin/dev advanced from a26615c50 to afbe1c5fe (#296) while validation was queued. I rebased the 12 J48 commits without conflicts and force-pushed the own branch to dd59582e8, now based on afbe1c5fe. The earlier compute01 run is on the old base and will not be reported as final. Fresh feature-on/off, fuzz, clippy, fmt, and hidden-hostname privacy checks are running/queued on dev-02; the root namespace check and a fresh compute01 scripts/verify fast remain next. Current dev-02 validation log: /home/claude/.cache/codex-scratch/j48/validate-slice.log.
Author
Owner

J48 progress: the Hickory library swap is now committed with a final descriptive subject and pushed on mesh-dns; current head is 2f77098ee7fba546d50f9de245504ab9fbd321b2. The code tree is unchanged from the successfully validated dd59582e8 tree.

Dev-02 validation completed with VALIDATION_EXIT=0; the log is /home/claude/.cache/codex-scratch/j48/validate-slice.log. It covers feature-on DNS/configuration/store tests, compile-out tests, the hidden-hostname mesh-view privacy regression, fuzz-target compile, clippy, and fmt. The DNS module documents slice-1 local-only service; its UDP regression checks remote REFUSED with no records and local resolution.

The first root namespace launch acquired a build slot but stopped before the test because Perl rejected the sudo environment's PATH; no test assertion ran. I identified a safe fixed PATH/HOME invocation and will rerun through CUA → sudo → build-slot after the current dev-02 queue clears. Compute01's previous gate is still progressing on its old base; I will let it finish, fetch/reset the VM worktree to this head, and run a fresh final scripts/verify fast.

Remaining estimate: about 60–90 minutes of active work plus shared build-slot waits for the root test and fresh final gate, then cleanup and final evidence.

J48 progress: the Hickory library swap is now committed with a final descriptive subject and pushed on `mesh-dns`; current head is `2f77098ee7fba546d50f9de245504ab9fbd321b2`. The code tree is unchanged from the successfully validated `dd59582e8` tree. Dev-02 validation completed with `VALIDATION_EXIT=0`; the log is `/home/claude/.cache/codex-scratch/j48/validate-slice.log`. It covers feature-on DNS/configuration/store tests, compile-out tests, the hidden-hostname mesh-view privacy regression, fuzz-target compile, clippy, and fmt. The DNS module documents slice-1 local-only service; its UDP regression checks remote `REFUSED` with no records and local resolution. The first root namespace launch acquired a build slot but stopped before the test because Perl rejected the sudo environment's `PATH`; no test assertion ran. I identified a safe fixed `PATH`/`HOME` invocation and will rerun through CUA → sudo → build-slot after the current dev-02 queue clears. Compute01's previous gate is still progressing on its old base; I will let it finish, fetch/reset the VM worktree to this head, and run a fresh final `scripts/verify fast`. Remaining estimate: about 60–90 minutes of active work plus shared build-slot waits for the root test and fresh final gate, then cleanup and final evidence.
Author
Owner

Root namespace test update: the earlier root-owned build-slot waiter lost its queue ticket because the queue's kill -0 stale-ticket check runs as claude. I verified it had no test process running and stopped those exact PIDs. I copied the already validated DNS integration-test binary to /tmp/j48-dns-root-test-bin, removed J48's two dev-02 target directories, and restored 36 GB free. A claude-owned build-slot reservation is queued; CUA will run sudo plus unshare against that binary while the reservation holds the slot. This test exercises the existing root namespace case without another Cargo build.

Root namespace test update: the earlier root-owned build-slot waiter lost its queue ticket because the queue's `kill -0` stale-ticket check runs as claude. I verified it had no test process running and stopped those exact PIDs. I copied the already validated DNS integration-test binary to `/tmp/j48-dns-root-test-bin`, removed J48's two dev-02 target directories, and restored 36 GB free. A claude-owned build-slot reservation is queued; CUA will run `sudo` plus `unshare` against that binary while the reservation holds the slot. This test exercises the existing root namespace case without another Cargo build.
Author
Owner

Correction to root namespace evidence: the first copied executable was the compile-out dns integration target. It exited 0 but reported running 0 tests, so it is not a passing root-test result. The feature-on target in the validation log is dns-0f01011c9b999903 and contains the ignored namespace test. I removed the temporary copy and dev-02 target trees; I will rebuild the feature-on --test dns --no-run target on the pushed head, then run that exact test via CUA sudo while a claude-owned build-slot reservation holds capacity. No production source changed.

Correction to root namespace evidence: the first copied executable was the compile-out `dns` integration target. It exited 0 but reported `running 0 tests`, so it is not a passing root-test result. The feature-on target in the validation log is `dns-0f01011c9b999903` and contains the ignored namespace test. I removed the temporary copy and dev-02 target trees; I will rebuild the feature-on `--test dns --no-run` target on the pushed head, then run that exact test via CUA sudo while a claude-owned build-slot reservation holds capacity. No production source changed.
Author
Owner

Compute01 stale-base gate update: the earlier run completed after 1 hour with FINAL_GATE_EXIT=1. The failure was session::user_host::tests::exhausted_pool_drains_live_ptys_during_random_session_churn (200 passed, 1 failed, 19 ignored); the static-build row passed. Its worktree was still on the pre-rebase head, so this is not final evidence. I will run scripts/verify fast on current head 2f77098ee; if the same test fails there, I will reproduce it on unmodified origin/dev before classifying it.

Compute01 stale-base gate update: the earlier run completed after 1 hour with `FINAL_GATE_EXIT=1`. The failure was `session::user_host::tests::exhausted_pool_drains_live_ptys_during_random_session_churn` (200 passed, 1 failed, 19 ignored); the static-build row passed. Its worktree was still on the pre-rebase head, so this is not final evidence. I will run `scripts/verify fast` on current head `2f77098ee`; if the same test fails there, I will reproduce it on unmodified `origin/dev` before classifying it.
Author
Owner

Root namespace test on dev-02 passed against the feature-on DNS test binary built from pushed head 2f77098ee7fba546d50f9de245504ab9fbd321b2. A claude-owned build-slot reservation held capacity while CUA ran sudo and unshare with the parent network-namespace guard. Verbatim result:

running 1 test
test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 12 filtered out; finished in 0.76s
ROOT_NAMESPACE_EXIT=0

Receipt: /tmp/j48-root-namespace-final.log on dev-02. The current-head compute01 scripts/verify fast is still running; its receipt is /home/claude/.cache/codex-scratch/j48/final-gate.log.

Root namespace test on dev-02 passed against the feature-on DNS test binary built from pushed head `2f77098ee7fba546d50f9de245504ab9fbd321b2`. A claude-owned build-slot reservation held capacity while CUA ran `sudo` and `unshare` with the parent network-namespace guard. Verbatim result: ```text running 1 test test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 12 filtered out; finished in 0.76s ROOT_NAMESPACE_EXIT=0 ``` Receipt: `/tmp/j48-root-namespace-final.log` on dev-02. The current-head compute01 `scripts/verify fast` is still running; its receipt is `/home/claude/.cache/codex-scratch/j48/final-gate.log`.
Author
Owner

J48 rebase update: while the current-head gate was running, origin/dev advanced from afbe1c5f to 65d98619 via the #293 merge. I rebased all 12 J48 commits without conflicts and force-pushed the branch to cef0d349a343b03c6f5a553ca26085cf67beace2, based on 65d9861914e8cd4a734d533d64ed1f4ea5c34119. The prior 2f77098ee gate passed, but I am rerunning the feature-on/off mesh validation on dev-02 and scripts/verify fast on compute01 against this new head before final evidence.

J48 rebase update: while the current-head gate was running, `origin/dev` advanced from `afbe1c5f` to `65d98619` via the #293 merge. I rebased all 12 J48 commits without conflicts and force-pushed the branch to `cef0d349a343b03c6f5a553ca26085cf67beace2`, based on `65d9861914e8cd4a734d533d64ed1f4ea5c34119`. The prior `2f77098ee` gate passed, but I am rerunning the feature-on/off mesh validation on dev-02 and `scripts/verify fast` on compute01 against this new head before final evidence.
Author
Owner

Current-head reruns have started on cef0d349a343b03c6f5a553ca26085cf67beace2 (base 65d9861914e8cd4a734d533d64ed1f4ea5c34119). Dev-02 is running the full J48 feature-on/off, fuzz-check, privacy, clippy and fmt script; log: /home/claude/.cache/codex-scratch/j48/validate-slice-current.log. I will run the root namespace test from its feature-on binary after that validation completes. Compute01 is running the fresh scripts/verify fast; log: /home/claude/.cache/codex-scratch/j48/final-gate.log. Both are launched through their machine build-slot queues.

Current-head reruns have started on `cef0d349a343b03c6f5a553ca26085cf67beace2` (base `65d9861914e8cd4a734d533d64ed1f4ea5c34119`). Dev-02 is running the full J48 feature-on/off, fuzz-check, privacy, clippy and fmt script; log: `/home/claude/.cache/codex-scratch/j48/validate-slice-current.log`. I will run the root namespace test from its feature-on binary after that validation completes. Compute01 is running the fresh `scripts/verify fast`; log: `/home/claude/.cache/codex-scratch/j48/final-gate.log`. Both are launched through their machine build-slot queues.
Author
Owner

J48 scope update: slice 1 stays local-only per the owner's direction. I am not adding the selective coordinator-to-resolver per-asker snapshot path (+2–3h) in this slice. Each resolver serves only its own accepted, grant-filtered snapshot when the UDP source is its own mesh IPv4; other sources receive REFUSED with no answer or authority records. The existing review_j68_remote_resolver_must_not_disclose_a_name_denied_to_the_asker regression checks the remote UDP refusal and local answer, and dns.rs documents that remote DNS requires a later per-asker snapshot slice. Remaining estimate: namespace test, final compute01 gate, cleanup and evidence, about 30–60 minutes including build-slot wait. Current head: cef0d349a343b03c6f5a553ca26085cf67beace2.

J48 scope update: slice 1 stays local-only per the owner's direction. I am not adding the selective coordinator-to-resolver per-asker snapshot path (+2–3h) in this slice. Each resolver serves only its own accepted, grant-filtered snapshot when the UDP source is its own mesh IPv4; other sources receive REFUSED with no answer or authority records. The existing `review_j68_remote_resolver_must_not_disclose_a_name_denied_to_the_asker` regression checks the remote UDP refusal and local answer, and `dns.rs` documents that remote DNS requires a later per-asker snapshot slice. Remaining estimate: namespace test, final compute01 gate, cleanup and evidence, about 30–60 minutes including build-slot wait. Current head: `cef0d349a343b03c6f5a553ca26085cf67beace2`.
Author
Owner

J48 complete on branch mesh-dns.

Head: cef0d349a3
Base: 65d9861914

Slice 1 keeps the resolver local-only: it answers queries sourced from its own mesh IPv4 using that node's accepted, grant-filtered view. Other sources receive REFUSED with zero answers and authority records. The selective coordinator-to-resolver per-asker snapshot path (+2–3h) is deferred; the dns.rs module doc records this limitation. Inventory and mesh-view API output are unchanged. The existing hidden-hostname mesh-view privacy regression still passes. User/group grants remain unexpressible in the current model and are recorded as a follow-up on #173.

J68 review fixes remain separate commits: remote-source refusal, explicit internal suffix override, and preservation of the .internal alias under a nested custom suffix. The Hickory-proto parser/encoder swap remains its own commit, with the fuzz target retained and mesh-dns-spike default-off.

Validation on dev-02: feature-on DNS/configuration/store suites, compile-out tests, hidden-hostname privacy regression, fuzz-target check, clippy and fmt passed; VALIDATION_EXIT=0. The DNS integration target reported 12 passed, 0 failed, 1 ignored, including review_j68_remote_resolver_must_not_disclose_a_name_denied_to_the_asker.

Root namespace test on the current head, via CUA sudo/unshare:

running 1 test
test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 12 filtered out; finished in 0.21s
ROOT_NAMESPACE_EXIT=0

The build-slot queue could not acquire a slot because dev-02 had 24 GB free, below its 25 GB floor, so I ran the already-built one-test binary directly via CUA sudo/unshare. An initial invocation omitted the required parent-netns environment variable and stopped before assertions; the corrected run above passed. Its temporary binary was removed.

scripts/verify fast on compute01 completed with FINAL_GATE_EXIT=0. Verbatim Rust gate table:

+---------------------------+--------------+
| Rust gate                 | Result       |
+---------------------------+--------------+
| fmt                       | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| real-binary fixture paths | PASS         |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+

Verbatim final gate output:

 39 pass
 0 fail
 59970 expect() calls
Ran 39 tests across 10 files. [7.48s]
FINAL_GATE_EXIT=0

Static size output (report-only under D243): x86_64 21,647,376 B vs 21,443,824 B baseline, delta +203,552 B; aarch64 19,795,608 B vs 19,629,240 B baseline, delta +166,368 B. The isolated parser comparison was simple-dns 519,992 B vs hickory-proto 759,192 B, delta +239,200 B.

Cleanup: J48 target directories are removed on the control worktree, dev-02 and compute01. The pushed branch is clean at the head above.

J48 complete on branch mesh-dns. Head: cef0d349a343b03c6f5a553ca26085cf67beace2 Base: 65d9861914e8cd4a734d533d64ed1f4ea5c34119 Slice 1 keeps the resolver local-only: it answers queries sourced from its own mesh IPv4 using that node's accepted, grant-filtered view. Other sources receive REFUSED with zero answers and authority records. The selective coordinator-to-resolver per-asker snapshot path (+2–3h) is deferred; the dns.rs module doc records this limitation. Inventory and mesh-view API output are unchanged. The existing hidden-hostname mesh-view privacy regression still passes. User/group grants remain unexpressible in the current model and are recorded as a follow-up on #173. J68 review fixes remain separate commits: remote-source refusal, explicit internal suffix override, and preservation of the .internal alias under a nested custom suffix. The Hickory-proto parser/encoder swap remains its own commit, with the fuzz target retained and mesh-dns-spike default-off. Validation on dev-02: feature-on DNS/configuration/store suites, compile-out tests, hidden-hostname privacy regression, fuzz-target check, clippy and fmt passed; VALIDATION_EXIT=0. The DNS integration target reported 12 passed, 0 failed, 1 ignored, including review_j68_remote_resolver_must_not_disclose_a_name_denied_to_the_asker. Root namespace test on the current head, via CUA sudo/unshare: ```text running 1 test test udp_resolver_answers_on_the_mesh_address_inside_a_network_namespace ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 12 filtered out; finished in 0.21s ROOT_NAMESPACE_EXIT=0 ``` The build-slot queue could not acquire a slot because dev-02 had 24 GB free, below its 25 GB floor, so I ran the already-built one-test binary directly via CUA sudo/unshare. An initial invocation omitted the required parent-netns environment variable and stopped before assertions; the corrected run above passed. Its temporary binary was removed. scripts/verify fast on compute01 completed with FINAL_GATE_EXIT=0. Verbatim Rust gate table: ```text +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | fmt | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | static builds | PASS | +---------------------------+--------------+ ``` Verbatim final gate output: ```text 39 pass 0 fail 59970 expect() calls Ran 39 tests across 10 files. [7.48s] FINAL_GATE_EXIT=0 ``` Static size output (report-only under D243): x86_64 21,647,376 B vs 21,443,824 B baseline, delta +203,552 B; aarch64 19,795,608 B vs 19,629,240 B baseline, delta +166,368 B. The isolated parser comparison was simple-dns 519,992 B vs hickory-proto 759,192 B, delta +239,200 B. Cleanup: J48 target directories are removed on the control worktree, dev-02 and compute01. The pushed branch is clean at the head above.
Author
Owner

J78 independent review round 2 starting. Review branch review-j78 at cef0d349a3; current origin/dev 0067e12554. Plan: inspect owner contracts and round-1 fixes, parser parity/bounds, atomic replacement and compile-out; run only targeted reproducers if needed. Estimate 45 minutes. Review-only: no code changed, full gate not applicable.

J78 independent review round 2 starting. Review branch review-j78 at cef0d349a343b03c6f5a553ca26085cf67beace2; current origin/dev 0067e1255407f0f3ed0f1d6c5e5caf51667e00a0. Plan: inspect owner contracts and round-1 fixes, parser parity/bounds, atomic replacement and compile-out; run only targeted reproducers if needed. Estimate 45 minutes. Review-only: no code changed, full gate not applicable.
Author
Owner

J78 independent review, round 2: REQUEST CHANGES

Target: mesh-dns at cef0d349a343b03c6f5a553ca26085cf67beace2, compared with current origin/dev at 0067e1255407f0f3ed0f1d6c5e5caf51667e00a0 (merge base 65d9861914e8cd4a734d533d64ed1f4ea5c34119). Read BRIEF-COMMON, PROCESS, ARCHITECTURE, D165/D197/D316, owner comments 72296/72299/72627/73114/73143, round-1 comment 73130, and J48's fixes and final receipts.

  1. P2: the parser swap breaks valid ASCII punycode hostnames and custom suffixes. crates/plugins/mesh/src/dns.rs:172–176, specifically .to_string() at line 174. Hickory renders a DNS name for display by decoding xn-- labels to Unicode. The coordinator validates and stores ASCII labels, so the rendered query no longer matches the snapshot or configured zone. A node named xn--bcher-kva receives NXDOMAIN for xn--bcher-kva.internal; with custom suffix xn--bcher-kva.example, laptop.xn--bcher-kva.example is treated as external and receives no response. Both configurations pass the production validate_dns rules and both queries resolved before the swap. Use the DNS name's ASCII representation for canonical matching, with regressions for both the hostname and suffix cases.

    Evidence: focused comparison on dev-02 under build-slot, nice -n10, Nix, -j3, importing the unmodified production dataplane.rs and current dns.rs plus HEAD^:dns.rs (simple-dns 0.13.0 vs hickory-proto 0.26.3). The 11 ordinary/malformed/EDNS/oversize comparison cases were equivalent, then both punycode probes failed:

    existing-behavior corpus: 11/11 equivalent
    xn--bcher-kva.internal: simple-dns NoError/1 answers, hickory Some((NXDomain, 0))
    laptop.xn--bcher-kva.example: simple-dns NoError/1 answers, hickory None
    assertion `left == right` failed: valid ASCII punycode labels must keep resolving after the parser swap
      left: 2
     right: 0
    REPRO_EXIT=101
    

Round-1 disposition: all three findings are fixed in source and have dedicated regressions. The UDP source guard checks this node's own mesh IPv4 before consulting its records; other valid sources get REFUSED with no answer or authority records. The local record set comes from the node's outbound-grant-filtered snapshot. The persisted explicitness bit honors a selected internal suffix, and zone matching preserves the .internal hostname alias beneath an overlapping custom suffix. J48's current-head validation receipt reports these targets passing (VALIDATION_EXIT=0, comment 73577).

No additional finding from atomic snapshot replacement or feature-off/compile-out inspection: chunks remain staged until final acceptance, record construction precedes publication, and a write lock replaces the entire record set. The listener, DNS module and optional hickory-proto dependency remain behind default-off mesh-dns-spike. The receive sentinel and 4096-byte parser limit remain. The fuzz target still imports the production parser and J48's current-head receipt confirms its compile check; no runtime fuzz receipt was provided, and I did not launch a separate fuzz campaign under this review's finding-only test scope. Size is report-only and not a finding.

Review regression: review-j78 at 436825fe324f33447831d915bc8d339e36e396a9 (local, not pushed), crates/plugins/mesh/tests/dns.rs::review_j78_ascii_punycode_labels_keep_resolving. Production code was not changed. The committed regression intentionally remains red; the focused source-comparison reproducer above establishes the failure without rebuilding the full application. Harness and receipt on the control host: /home/kayg/.local/state/infraternal-orchestrator/j78/repro/ and /home/kayg/.local/state/infraternal-orchestrator/j78/repro.log.

scripts/verify fast: not run, per the explicit review-only instruction to run tests only to confirm a finding. J48's implementation gate receipt is in comment 73577 (FINAL_GATE_EXIT=0); that is author evidence, not a J78 gate run. All J78 commands have completed; the J78 VM target directory was removed. No pending J78 run.

## J78 independent review, round 2: REQUEST CHANGES Target: `mesh-dns` at `cef0d349a343b03c6f5a553ca26085cf67beace2`, compared with current `origin/dev` at `0067e1255407f0f3ed0f1d6c5e5caf51667e00a0` (merge base `65d9861914e8cd4a734d533d64ed1f4ea5c34119`). Read BRIEF-COMMON, PROCESS, ARCHITECTURE, D165/D197/D316, owner comments 72296/72299/72627/73114/73143, round-1 comment 73130, and J48's fixes and final receipts. 1. **P2: the parser swap breaks valid ASCII punycode hostnames and custom suffixes.** `crates/plugins/mesh/src/dns.rs:172–176`, specifically `.to_string()` at line 174. Hickory renders a DNS name for display by decoding `xn--` labels to Unicode. The coordinator validates and stores ASCII labels, so the rendered query no longer matches the snapshot or configured zone. A node named `xn--bcher-kva` receives NXDOMAIN for `xn--bcher-kva.internal`; with custom suffix `xn--bcher-kva.example`, `laptop.xn--bcher-kva.example` is treated as external and receives no response. Both configurations pass the production `validate_dns` rules and both queries resolved before the swap. Use the DNS name's ASCII representation for canonical matching, with regressions for both the hostname and suffix cases. Evidence: focused comparison on dev-02 under `build-slot`, `nice -n10`, Nix, `-j3`, importing the unmodified production `dataplane.rs` and current `dns.rs` plus `HEAD^:dns.rs` (simple-dns 0.13.0 vs hickory-proto 0.26.3). The 11 ordinary/malformed/EDNS/oversize comparison cases were equivalent, then both punycode probes failed: ```text existing-behavior corpus: 11/11 equivalent xn--bcher-kva.internal: simple-dns NoError/1 answers, hickory Some((NXDomain, 0)) laptop.xn--bcher-kva.example: simple-dns NoError/1 answers, hickory None assertion `left == right` failed: valid ASCII punycode labels must keep resolving after the parser swap left: 2 right: 0 REPRO_EXIT=101 ``` Round-1 disposition: all three findings are fixed in source and have dedicated regressions. The UDP source guard checks this node's own mesh IPv4 before consulting its records; other valid sources get REFUSED with no answer or authority records. The local record set comes from the node's outbound-grant-filtered snapshot. The persisted explicitness bit honors a selected `internal` suffix, and zone matching preserves the `.internal` hostname alias beneath an overlapping custom suffix. J48's current-head validation receipt reports these targets passing (`VALIDATION_EXIT=0`, comment 73577). No additional finding from atomic snapshot replacement or feature-off/compile-out inspection: chunks remain staged until final acceptance, record construction precedes publication, and a write lock replaces the entire record set. The listener, DNS module and optional hickory-proto dependency remain behind default-off `mesh-dns-spike`. The receive sentinel and 4096-byte parser limit remain. The fuzz target still imports the production parser and J48's current-head receipt confirms its compile check; no runtime fuzz receipt was provided, and I did not launch a separate fuzz campaign under this review's finding-only test scope. Size is report-only and not a finding. Review regression: `review-j78` at `436825fe324f33447831d915bc8d339e36e396a9` (local, not pushed), `crates/plugins/mesh/tests/dns.rs::review_j78_ascii_punycode_labels_keep_resolving`. Production code was not changed. The committed regression intentionally remains red; the focused source-comparison reproducer above establishes the failure without rebuilding the full application. Harness and receipt on the control host: `/home/kayg/.local/state/infraternal-orchestrator/j78/repro/` and `/home/kayg/.local/state/infraternal-orchestrator/j78/repro.log`. `scripts/verify fast`: not run, per the explicit review-only instruction to run tests only to confirm a finding. J48's implementation gate receipt is in comment 73577 (`FINAL_GATE_EXIT=0`); that is author evidence, not a J78 gate run. All J78 commands have completed; the J78 VM target directory was removed. No pending J78 run.
Author
Owner

J78 fix starts on mesh-dns at cef0d349a3; current origin/dev is 0067e12554. Plan: rebase onto current dev, apply reviewer test 436825fe3 and record the focused RED result, change only the DNS query-name conversion to Hickory's ASCII form, run the focused DNS suite and one full scripts/verify fast gate, then commit and push. Estimate: about 45 minutes including the gate queue.

J78 fix starts on mesh-dns at cef0d349a343b03c6f5a553ca26085cf67beace2; current origin/dev is 0067e1255407f0f3ed0f1d6c5e5caf51667e00a0. Plan: rebase onto current dev, apply reviewer test 436825fe3 and record the focused RED result, change only the DNS query-name conversion to Hickory's ASCII form, run the focused DNS suite and one full scripts/verify fast gate, then commit and push. Estimate: about 45 minutes including the gate queue.
Author
Owner

J78 RED evidence: after rebasing mesh-dns onto origin/dev 0067e12554, I applied reviewer test 436825fe3 without production changes and ran it on dev-02 through build-slot.

Command: cargo test --locked --jobs 3 -p infraternal-plugin-mesh --features mesh-dns-spike --test dns review_j78_ascii_punycode_labels_keep_resolving -- --exact --nocapture

running 1 test

thread 'review_j78_ascii_punycode_labels_keep_resolving' panicked at crates/plugins/mesh/tests/dns.rs:599:9:
assertion `left == right` failed: xn--bcher-kva.internal
  left: NXDomain
 right: NoError
test review_j78_ascii_punycode_labels_keep_resolving ... FAILED
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 13 filtered out; finished in 0.00s
RED_EXIT=101

This confirms the display-form conversion breaks ASCII punycode matching. Next: use Hickory's ASCII form in dns.rs:174 and rerun the regression.

J78 RED evidence: after rebasing mesh-dns onto origin/dev 0067e1255407f0f3ed0f1d6c5e5caf51667e00a0, I applied reviewer test 436825fe3 without production changes and ran it on dev-02 through build-slot. Command: cargo test --locked --jobs 3 -p infraternal-plugin-mesh --features mesh-dns-spike --test dns review_j78_ascii_punycode_labels_keep_resolving -- --exact --nocapture ```text running 1 test thread 'review_j78_ascii_punycode_labels_keep_resolving' panicked at crates/plugins/mesh/tests/dns.rs:599:9: assertion `left == right` failed: xn--bcher-kva.internal left: NXDomain right: NoError test review_j78_ascii_punycode_labels_keep_resolving ... FAILED test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 13 filtered out; finished in 0.00s RED_EXIT=101 ``` This confirms the display-form conversion breaks ASCII punycode matching. Next: use Hickory's ASCII form in dns.rs:174 and rerun the regression.
Author
Owner

J78 GREEN evidence: changed only the query-name conversion in dns.rs to Hickory's ASCII representation before the existing lowercase match. The reviewer regression now passes both ASCII punycode host and custom-suffix cases.

Focused command on dev-02 through build-slot:

test review_j78_ascii_punycode_labels_keep_resolving ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 13 filtered out; finished in 0.00s
GREEN_EXIT=0

Full feature-on DNS target:

test result: ok. 13 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.01s
DNS_SUITE_EXIT=0

Next: commit this regression and the one-function fix together, then run scripts/verify fast on dev-02.

J78 GREEN evidence: changed only the query-name conversion in dns.rs to Hickory's ASCII representation before the existing lowercase match. The reviewer regression now passes both ASCII punycode host and custom-suffix cases. Focused command on dev-02 through build-slot: ```text test review_j78_ascii_punycode_labels_keep_resolving ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 13 filtered out; finished in 0.00s GREEN_EXIT=0 ``` Full feature-on DNS target: ```text test result: ok. 13 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.01s DNS_SUITE_EXIT=0 ``` Next: commit this regression and the one-function fix together, then run scripts/verify fast on dev-02.
Author
Owner

J78 fix and the reviewer regression are together in atomic commit bfc29a7ea9, pushed to mesh-dns. The focused regression and full feature-on DNS target pass on dev-02. I am starting the full scripts/verify fast gate on dev-02 through build-slot; receipt will be /home/claude/.cache/codex-scratch/j78/final-gate.log.

J78 fix and the reviewer regression are together in atomic commit bfc29a7ea9d7f28fe2807a4bb6aaf9f5a9b9c6dc, pushed to mesh-dns. The focused regression and full feature-on DNS target pass on dev-02. I am starting the full scripts/verify fast gate on dev-02 through build-slot; receipt will be /home/claude/.cache/codex-scratch/j78/final-gate.log.
Author
Owner

Rebased the J78 branch onto current origin/dev 6093cb8a8 and amended the single J78 fix commit to include rustfmt's required formatting for the reviewer regression: f4a43bcb39 (pushed). The production change remains the one query-name conversion to Hickory's ASCII form. Running the focused regression and fresh scripts/verify fast on dev-02 now; logs will be under /home/claude/.cache/codex-scratch/j78/.

Rebased the J78 branch onto current origin/dev 6093cb8a8 and amended the single J78 fix commit to include rustfmt's required formatting for the reviewer regression: f4a43bcb39a80dcf9c5825ad2e12e04673ed960d (pushed). The production change remains the one query-name conversion to Hickory's ASCII form. Running the focused regression and fresh scripts/verify fast on dev-02 now; logs will be under /home/claude/.cache/codex-scratch/j78/.
Author
Owner

Fresh J78 gate on f4a43bcb39: fmt, compile-out, clippy, architecture, and static builds PASS; test stage exits 101. Two test failures are outside the DNS files: restart::tests::default_restart_opens_the_receiver_target_exactly_once (61.03s) and real_binary_preserves_input_and_output_across_24_abrupt_stops (subprocess/fixture timeouts). Static outputs: x86_64 21,732,912 B (+289,088 vs gate baseline); aarch64 19,857,352 B (+228,112). I am checking the two failing tests on unmodified origin/dev 6093cb8a8 before classifying. Full gate log: /home/claude/.cache/codex-scratch/j78/final-gate.log on dev-02.

Fresh J78 gate on f4a43bcb39a80dcf9c5825ad2e12e04673ed960d: fmt, compile-out, clippy, architecture, and static builds PASS; test stage exits 101. Two test failures are outside the DNS files: restart::tests::default_restart_opens_the_receiver_target_exactly_once (61.03s) and real_binary_preserves_input_and_output_across_24_abrupt_stops (subprocess/fixture timeouts). Static outputs: x86_64 21,732,912 B (+289,088 vs gate baseline); aarch64 19,857,352 B (+228,112). I am checking the two failing tests on unmodified origin/dev 6093cb8a8 before classifying. Full gate log: /home/claude/.cache/codex-scratch/j78/final-gate.log on dev-02.
Author
Owner

J78 fix complete on mesh-dns, rebased onto origin/dev 6093cb8a8 and pushed at f4a43bcb39.

The lookup key now uses Hickory's ASCII form before trimming and lowercasing. The review regression covers both xn--bcher-kva.internal and the punycode custom suffix. The focused regression passes (1 passed).

Full scripts/verify fast on dev-02 reported:

text
+---------------------------+--------------+
| Rust gate | Result |
+---------------------------+--------------+
| fmt | PASS |
| VT native fmt | PASS |
| CLI plugin compile-out | PASS |
| clippy | PASS |
| SSH runtime test helper | PASS |
| real-binary fixture paths | PASS |
| test | FAIL (101) |
| SSH inode reuse fixture | PASS |
| shim process-per-user | PASS |
| cargo-deny | PASS |
| gate architecture | PASS |
| static builds | PASS |
+---------------------------+--------------+

FINAL_GATE_EXIT=1

The two test-stage failures were restart::tests::default_restart_opens_the_receiver_target_exactly_once (61.03s timeout) and real_binary_preserves_input_and_output_across_24_abrupt_stops (fixture subprocess timeouts). Each focused test passed individually on unmodified origin/dev 6093cb8a8; this did not reproduce the full-suite failures, and I did not run a full baseline gate.

Static measurements from the gate: x86_64 21,732,912 B (baseline 21,443,824; +289,088 B) and aarch64 19,857,352 B (baseline 19,629,240; +228,112 B), report-only under D243.

Focused and baseline logs are in /home/claude/.cache/codex-scratch/j78/ on dev-02. J78 target directories and the temporary origin/dev worktree were removed.

J78 fix complete on mesh-dns, rebased onto origin/dev 6093cb8a8 and pushed at f4a43bcb39a80dcf9c5825ad2e12e04673ed960d. The lookup key now uses Hickory's ASCII form before trimming and lowercasing. The review regression covers both xn--bcher-kva.internal and the punycode custom suffix. The focused regression passes (1 passed). Full scripts/verify fast on dev-02 reported: text +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | fmt | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | test | FAIL (101) | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | static builds | PASS | +---------------------------+--------------+ FINAL_GATE_EXIT=1 The two test-stage failures were restart::tests::default_restart_opens_the_receiver_target_exactly_once (61.03s timeout) and real_binary_preserves_input_and_output_across_24_abrupt_stops (fixture subprocess timeouts). Each focused test passed individually on unmodified origin/dev 6093cb8a8; this did not reproduce the full-suite failures, and I did not run a full baseline gate. Static measurements from the gate: x86_64 21,732,912 B (baseline 21,443,824; +289,088 B) and aarch64 19,857,352 B (baseline 19,629,240; +228,112 B), report-only under D243. Focused and baseline logs are in /home/claude/.cache/codex-scratch/j78/ on dev-02. J78 target directories and the temporary origin/dev worktree were removed.
Author
Owner

J254 start. Branch: mesh-dns-j254; base: origin/dev 5ea05cc2e1f174ce47cc430bd13e9457ae91f29c.

Plan: read all #266 history and audit the existing Hickory DNS path against D327/D339/D352 and mesh decisions D164/D215/D235; add failing-first tests for indistinguishable denied-name NXDOMAIN and naming; enable shipped DNS and fix feature-set test coverage; add resolver install/removal for Linux and macOS with safe fallbacks; prove two Linux nodes and the Mac; measure static size, query p50/p99 under load, and memory per 1,000 names; run quick, fast, privileged, and platform gates. Estimate: about 12 hours plus VM queue time. No decision changes are planned.

Starting with repository/history review. I will post each finding, its evidence, and final gate output here.

J254 start. Branch: `mesh-dns-j254`; base: `origin/dev` `5ea05cc2e1f174ce47cc430bd13e9457ae91f29c`. Plan: read all #266 history and audit the existing Hickory DNS path against D327/D339/D352 and mesh decisions D164/D215/D235; add failing-first tests for indistinguishable denied-name NXDOMAIN and naming; enable shipped DNS and fix feature-set test coverage; add resolver install/removal for Linux and macOS with safe fallbacks; prove two Linux nodes and the Mac; measure static size, query p50/p99 under load, and memory per 1,000 names; run quick, fast, privileged, and platform gates. Estimate: about 12 hours plus VM queue time. No decision changes are planned. Starting with repository/history review. I will post each finding, its evidence, and final gate output here.
Author
Owner

J254 audit findings (source at base 5ea05cc2e1f174ce47cc430bd13e9457ae91f29c):

  1. D327 response-equality coverage is missing. J48’s compiler filters peer_names by the receiver’s outbound policy (crates/plugins/mesh/src/configuration.rs), and dns.rs returns authoritative NXDOMAIN with the configured SOA for any absent label. Existing tests check NXDOMAIN and the five-second SOA separately, but none compares a denied peer name with an unrelated nonexistent name. I will add a failing-first regression comparing the response code and serialized SOA/RR bytes, and benchmark both lookup classes; functional tests will not assert wall-clock speed.

  2. The feature is still a development spike and has no resolver routing. crates/plugins/mesh/Cargo.toml defines default-off mesh-dns-spike; lib.rs compiles dns only for Linux; node.rs owns the listener only on Linux. I found no resolvectl, systemd-resolved, /etc/resolver, or resolv.conf integration in crates/. macOS snapshots carry DNS names/suffixes in macos_node.rs, but there is no Mac resolver. This prevents the requested system lookup and clean resolver removal on either platform.

  3. J208c fixture check. The current tests/dns.rs::config already sets exposure: None, which is the missing-field fix called out in #221 comment 79397. I will run cargo test -p infraternal-plugin-mesh --all-features --locked --test dns on this base and again after the feature rename.

J254 audit findings (source at base `5ea05cc2e1f174ce47cc430bd13e9457ae91f29c`): 1. **D327 response-equality coverage is missing.** J48’s compiler filters `peer_names` by the receiver’s outbound policy (`crates/plugins/mesh/src/configuration.rs`), and `dns.rs` returns authoritative NXDOMAIN with the configured SOA for any absent label. Existing tests check NXDOMAIN and the five-second SOA separately, but none compares a denied peer name with an unrelated nonexistent name. I will add a failing-first regression comparing the response code and serialized SOA/RR bytes, and benchmark both lookup classes; functional tests will not assert wall-clock speed. 2. **The feature is still a development spike and has no resolver routing.** `crates/plugins/mesh/Cargo.toml` defines default-off `mesh-dns-spike`; `lib.rs` compiles `dns` only for Linux; `node.rs` owns the listener only on Linux. I found no `resolvectl`, systemd-resolved, `/etc/resolver`, or `resolv.conf` integration in `crates/`. macOS snapshots carry DNS names/suffixes in `macos_node.rs`, but there is no Mac resolver. This prevents the requested system lookup and clean resolver removal on either platform. 3. **J208c fixture check.** The current `tests/dns.rs::config` already sets `exposure: None`, which is the missing-field fix called out in #221 comment 79397. I will run `cargo test -p infraternal-plugin-mesh --all-features --locked --test dns` on this base and again after the feature rename.
Author
Owner

J254 finding (D327): crates/plugins/mesh/src/dns.rs::response_for_source returns REFUSED without the negative SOA whenever a query arrives from a source other than the local mesh address. The regression review_j68_remote_resolver_must_not_disclose_a_name_denied_to_the_asker confirms the remote query gets REFUSED while the local query gets the hidden node’s A record. That differs from an absent name and leaks the visibility boundary. Next I am adding a failing-first test that compares the remote denied-name response with a remote nonexistent-name response, including the SOA wire section.

J254 finding (D327): `crates/plugins/mesh/src/dns.rs::response_for_source` returns `REFUSED` without the negative SOA whenever a query arrives from a source other than the local mesh address. The regression `review_j68_remote_resolver_must_not_disclose_a_name_denied_to_the_asker` confirms the remote query gets REFUSED while the local query gets the hidden node’s A record. That differs from an absent name and leaks the visibility boundary. Next I am adding a failing-first test that compares the remote denied-name response with a remote nonexistent-name response, including the SOA wire section.
Author
Owner

J254 progress: a VM fetch advanced origin/dev from the handoff SHA 5ea05cc2 to e6cf4661 while I was preparing the test run. I rebased the clean task branch onto current origin/dev and preserved the uncommitted failing-first DNS test. The VM J254 worktree is based on the same new head; I am beginning the required verify quick pre-check there before the targeted integration test.

J254 progress: a VM fetch advanced `origin/dev` from the handoff SHA 5ea05cc2 to e6cf4661 while I was preparing the test run. I rebased the clean task branch onto current `origin/dev` and preserved the uncommitted failing-first DNS test. The VM J254 worktree is based on the same new head; I am beginning the required `verify quick` pre-check there before the targeted integration test.
Author
Owner

J254 finding from the first scripts/verify quick on dev-02: shape lints off fails test_no_local_exceptions with crates/infraternal-server/src/mesh_ssh.rs, outside this branch diff; I will verify the same check against clean e6cf4661 before attributing it to base. The same quick run flagged D345 docs on the edited DNS test helper and regression; I added those comments. Full gate output is saved at dev-02:/home/claude/.cache/codex-scratch/j254/evidence/verify-quick.log (run exited 1).

J254 finding from the first `scripts/verify quick` on dev-02: `shape lints off` fails `test_no_local_exceptions` with `crates/infraternal-server/src/mesh_ssh.rs`, outside this branch diff; I will verify the same check against clean e6cf4661 before attributing it to base. The same quick run flagged D345 docs on the edited DNS test helper and regression; I added those comments. Full gate output is saved at `dev-02:/home/claude/.cache/codex-scratch/j254/evidence/verify-quick.log` (run exited 1).
Author
Owner

Correction to my previous quick-gate finding: dev-02 had advanced origin/dev from e6cf4661 to f9f8ced3 after the J254 worktree was created. I ran python3 scripts/test-shape-lints.py with the worktree detached at the clean current origin/dev; all 4 tests passed (artifact: dev-02:/home/claude/.cache/codex-scratch/j254/evidence/shape-lints-origin-dev.log). The quick failure was therefore stale-base evidence, not a current-base blocker. I am rebasing both task worktrees to the latest origin/dev and will rerun quick.

Correction to my previous quick-gate finding: dev-02 had advanced `origin/dev` from e6cf4661 to f9f8ced3 after the J254 worktree was created. I ran `python3 scripts/test-shape-lints.py` with the worktree detached at the clean current `origin/dev`; all 4 tests passed (artifact: `dev-02:/home/claude/.cache/codex-scratch/j254/evidence/shape-lints-origin-dev.log`). The quick failure was therefore stale-base evidence, not a current-base blocker. I am rebasing both task worktrees to the latest origin/dev and will rerun quick.
Author
Owner

J254 progress: current-base verify quick (PID 1161801) is second in dev-02 queue; I will queue the exact J208c cargo test -p infraternal-plugin-mesh --all-features --locked --test dns behind it, using the same worktree target. Both Linux VMs report active systemd-resolved and no NetworkManager; the implementation will use per-link route domains through resolved and provide a per-link NetworkManager fallback for hosts without resolved.

J254 progress: current-base `verify quick` (PID 1161801) is second in dev-02 queue; I will queue the exact J208c `cargo test -p infraternal-plugin-mesh --all-features --locked --test dns` behind it, using the same worktree target. Both Linux VMs report active systemd-resolved and no NetworkManager; the implementation will use per-link route domains through resolved and provide a per-link NetworkManager fallback for hosts without resolved.
Author
Owner

J254 queue update: the current-base verify quick has waited ~12 minutes in dev-02 build-slot (PID 1161801), with two active jobs at ~17 and ~16 minutes and three commands ahead. The exact J208c DNS test is queued behind quick. compute01 has a longer queue, so I am keeping both runs on dev-02 and will continue waiting in the foreground. Quick log: dev-02:/home/claude/.cache/codex-scratch/j254/evidence/verify-quick-current.log.

J254 queue update: the current-base `verify quick` has waited ~12 minutes in dev-02 build-slot (PID 1161801), with two active jobs at ~17 and ~16 minutes and three commands ahead. The exact J208c DNS test is queued behind quick. compute01 has a longer queue, so I am keeping both runs on dev-02 and will continue waiting in the foreground. Quick log: `dev-02:/home/claude/.cache/codex-scratch/j254/evidence/verify-quick-current.log`.
Author
Owner

J254 WIP checkpoint: commit af0e39c0b adds only the D327 DNS regression and is pushed on mesh-dns-j254. The current-base quick gate and exact all-features DNS test remain queued on dev-02; no production code has been changed yet. After the test confirms RED, I will implement the minimal response-path change and checkpoint that green slice.

J254 WIP checkpoint: commit `af0e39c0b` adds only the D327 DNS regression and is pushed on `mesh-dns-j254`. The current-base quick gate and exact all-features DNS test remain queued on dev-02; no production code has been changed yet. After the test confirms RED, I will implement the minimal response-path change and checkpoint that green slice.
Author
Owner

J254 finding: D327 needs a negative answer after the source check; the current early REFUSED leaks that the name is handled. Compute01 runs systemd 257.13, and its resolvectl man page accepts DNS server addresses with a custom port, so the existing 1053 listener can use per-link routing without a global resolver change. The named dev-02 and Mac VM links are pending; the existing foreground checks remain queued.

J254 finding: D327 needs a negative answer after the source check; the current early REFUSED leaks that the name is handled. Compute01 runs systemd 257.13, and its resolvectl man page accepts DNS server addresses with a custom port, so the existing 1053 listener can use per-link routing without a global resolver change. The named dev-02 and Mac VM links are pending; the existing foreground checks remain queued.
Author
Owner

J254 evidence: scripts/verify quick on the test-only head af0e39c0b passed. Output: fmt PASS; doc coverage ratchet PASS; shape lints off (D351) PASS; doc comments on diff PASS; fixed test waits PASS; clippy (workspace) PASS; unit tests (changed) PASS; QUICK pre-check passed. This is not the final gate. The focused DNS integration test is still running.

J254 evidence: scripts/verify quick on the test-only head af0e39c0b passed. Output: fmt PASS; doc coverage ratchet PASS; shape lints off (D351) PASS; doc comments on diff PASS; fixed test waits PASS; clippy (workspace) PASS; unit tests (changed) PASS; QUICK pre-check passed. This is not the final gate. The focused DNS integration test is still running.
Author
Owner

J254 finding: the new test initially failed before reaching the resolver. Current configuration::compile returns None for server.peers[2].inbound without an explicit peer-2-to-server arrow. I am making the query path explicitly reachable while keeping the separate denial assertion, so the rerun exercises the D327 response rather than a network-policy block.

J254 finding: the new test initially failed before reaching the resolver. Current `configuration::compile` returns `None` for `server.peers[2].inbound` without an explicit peer-2-to-server arrow. I am making the query path explicitly reachable while keeping the separate denial assertion, so the rerun exercises the D327 response rather than a network-policy block.
Author
Owner

J254 update: corrected the regression fixture in f77a85ba4 and pushed it. The test now grants peer 2 access to the DNS server explicitly, then checks that the same peer cannot learn peer 3s name. I am queuing the focused rerun against this head; no production behavior has changed yet.

J254 update: corrected the regression fixture in f77a85ba4 and pushed it. The test now grants peer 2 access to the DNS server explicitly, then checks that the same peer cannot learn peer 3s name. I am queuing the focused rerun against this head; no production behavior has changed yet.
Author
Owner

J254 Mac evidence: under /tmp/mac-bench.lock, macOS 26.5.2 accepted a temporary /etc/resolver/j254-probe.invalid file with nameserver 127.0.0.1, port 1053, and search j254-probe.invalid. scutil --dns reported the exact domain, port, and per-resolver search domain. A domain directive in the file prevented it from appearing; the filename supplies the domain. The probe and newly created directory were removed. The lume account is UID 501 in admin, and sudo -n id -u returned 0, so the userspace node can use its privileged lifecycle here; unprivileged direct writes remain unavailable.

J254 Mac evidence: under /tmp/mac-bench.lock, macOS 26.5.2 accepted a temporary /etc/resolver/j254-probe.invalid file with nameserver 127.0.0.1, port 1053, and search j254-probe.invalid. scutil --dns reported the exact domain, port, and per-resolver search domain. A domain directive in the file prevented it from appearing; the filename supplies the domain. The probe and newly created directory were removed. The lume account is UID 501 in admin, and sudo -n id -u returned 0, so the userspace node can use its privileged lifecycle here; unprivileged direct writes remain unavailable.
Author
Owner

J254 resuming on mesh-dns-j254. Branch base: f9f8ced338 (current origin/dev: ddfbb291bf). Head: f77a85ba43. The explicit peer-2-to-server fixture edge is pushed; next I am rerunning verify quick on dev-02 to confirm the D327 regression reaches the response check. Plan: D327 red/green and default-on feature cleanup; split-DNS lifecycle on Linux and macOS; real-machine resolution/removal proof; size, p50/p99, and 1,000-name memory measurements; quick/fast/privileged gates. VM build slots are full (dev-02: 3 queued; compute01: 7 queued), so the first gate will wait for a slot. Estimate: 6-8 hours of VM work, dependent on queue and real-node access.

J254 resuming on mesh-dns-j254. Branch base: f9f8ced338278d924803a691f9636a9519babcb2 (current origin/dev: ddfbb291bfb1f11e64222564618e8f0cf4291b04). Head: f77a85ba43351111b535c94d613957c8d0a62746. The explicit peer-2-to-server fixture edge is pushed; next I am rerunning verify quick on dev-02 to confirm the D327 regression reaches the response check. Plan: D327 red/green and default-on feature cleanup; split-DNS lifecycle on Linux and macOS; real-machine resolution/removal proof; size, p50/p99, and 1,000-name memory measurements; quick/fast/privileged gates. VM build slots are full (dev-02: 3 queued; compute01: 7 queued), so the first gate will wait for a slot. Estimate: 6-8 hours of VM work, dependent on queue and real-node access.
Author
Owner

J254 finding/update: scripts/verify quick passed on dev-02 at f77a85ba4. Scope was workspace=0, changed packages=[infraternal-plugin-mesh], tracer=0; workspace clippy passed and the mesh unit binary reported 34 passed, 0 failed, 0 ignored. Verbatim quick footer: "QUICK pre-check passed. This is not the gate: run scripts/verify fast before reporting done." The quick row does not run crates/plugins/mesh/tests/dns.rs, so I am now running denied_remote_names_match_the_nonexistent_name_response directly before changing production code.

J254 finding/update: scripts/verify quick passed on dev-02 at f77a85ba4. Scope was workspace=0, changed packages=[infraternal-plugin-mesh], tracer=0; workspace clippy passed and the mesh unit binary reported 34 passed, 0 failed, 0 ignored. Verbatim quick footer: "QUICK pre-check passed. This is not the gate: run scripts/verify fast before reporting done." The quick row does not run crates/plugins/mesh/tests/dns.rs, so I am now running denied_remote_names_match_the_nonexistent_name_response directly before changing production code.
Author
Owner

D327 audit finding (source evidence): crates/plugins/mesh/src/dns.rs::response_for_source rejects every nonlocal source before matching the zone or building the negative SOA, so hidden and nonexistent remote names get REFUSED with no SOA. configuration::compile_inner already omits a peer name when that node’s outbound policy is None, so the requester’s accepted local snapshot treats both hidden and absent names as the same NXDOMAIN path. The corrected denied_remote_names_match_the_nonexistent_name_response test is queued on dev-02 to confirm the listener-side mismatch before the fix.

D327 audit finding (source evidence): `crates/plugins/mesh/src/dns.rs::response_for_source` rejects every nonlocal source before matching the zone or building the negative SOA, so hidden and nonexistent remote names get `REFUSED` with no SOA. `configuration::compile_inner` already omits a peer name when that node’s outbound policy is `None`, so the requester’s accepted local snapshot treats both hidden and absent names as the same NXDOMAIN path. The corrected `denied_remote_names_match_the_nonexistent_name_response` test is queued on dev-02 to confirm the listener-side mismatch before the fix.
Author
Owner

D327 red evidence: the corrected focused test reached the response assertion and failed as expected. Command: nice -n10 nix develop -c cargo test -p infraternal-plugin-mesh --features agent,server,mesh-dns-spike --test dns denied_remote_names_match_the_nonexistent_name_response -- --nocapture on dev-02. Exact failure: assertion left == right failed: a name hidden from the asker must look nonexistent followed by left: Refused / right: NXDomain; test result 0 passed, 1 failed. I am fixing the early nonlocal-source REFUSED path to return the same in-zone negative answer/SOA helper used for missing names.

D327 red evidence: the corrected focused test reached the response assertion and failed as expected. Command: `nice -n10 nix develop -c cargo test -p infraternal-plugin-mesh --features agent,server,mesh-dns-spike --test dns denied_remote_names_match_the_nonexistent_name_response -- --nocapture` on dev-02. Exact failure: `assertion left == right failed: a name hidden from the asker must look nonexistent` followed by `left: Refused` / `right: NXDomain`; test result 0 passed, 1 failed. I am fixing the early nonlocal-source REFUSED path to return the same in-zone negative answer/SOA helper used for missing names.
Author
Owner

D327 green evidence: the post-fix DNS integration target passed on dev-02: test result: ok. 13 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.01s. The ignored test is the existing root network-namespace test. Captured output: /home/claude/.cache/codex-scratch/j254/evidence/dns-tests.log. The focused regression also passed individually. I am committing the shared NXDOMAIN/SOA path now; next is default-on feature promotion and the resolver lifecycle.

D327 green evidence: the post-fix DNS integration target passed on dev-02: `test result: ok. 13 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.01s`. The ignored test is the existing root network-namespace test. Captured output: `/home/claude/.cache/codex-scratch/j254/evidence/dns-tests.log`. The focused regression also passed individually. I am committing the shared NXDOMAIN/SOA path now; next is default-on feature promotion and the resolver lifecycle.
Author
Owner

J254 pushed c702c09c1 to mesh-dns-j254. The D327 fix makes remote in-zone queries and names missing from the accepted local snapshot use one NXDOMAIN/SOA path. Focused regression: passed. Complete DNS target: test result: ok. 13 passed; 0 failed; 1 ignored; captured at /home/claude/.cache/codex-scratch/j254/evidence/dns-tests.log. Next I am rerunning scripts/verify quick against the pushed fix, then I will promote the spike to the real mesh-dns feature and repair all explicit test feature sets.

J254 pushed `c702c09c1` to `mesh-dns-j254`. The D327 fix makes remote in-zone queries and names missing from the accepted local snapshot use one NXDOMAIN/SOA path. Focused regression: passed. Complete DNS target: `test result: ok. 13 passed; 0 failed; 1 ignored`; captured at `/home/claude/.cache/codex-scratch/j254/evidence/dns-tests.log`. Next I am rerunning `scripts/verify quick` against the pushed fix, then I will promote the spike to the real `mesh-dns` feature and repair all explicit test feature sets.
Author
Owner

Post-D327 scripts/verify quick passed at c702c09c1 on compute01. Verbatim gate footer: QUICK pre-check passed. This is not the gate: run scripts/verify fast before reporting done. The result table was PASS for scope guard, migration names, fmt, doc coverage ratchet, shape lints off, doc comments on diff, fixed test waits, workspace clippy, and changed-package unit tests (34 passed, 0 failed, 0 ignored). Full captured output: /home/claude/.cache/codex-scratch/j254/evidence/quick-c702.log. I am now checking the default-on feature behavior and the explicit DNS test feature set before renaming the spike.

Post-D327 `scripts/verify quick` passed at c702c09c1 on compute01. Verbatim gate footer: `QUICK pre-check passed. This is not the gate: run scripts/verify fast before reporting done.` The result table was PASS for scope guard, migration names, fmt, doc coverage ratchet, shape lints off, doc comments on diff, fixed test waits, workspace clippy, and changed-package unit tests (34 passed, 0 failed, 0 ignored). Full captured output: `/home/claude/.cache/codex-scratch/j254/evidence/quick-c702.log`. I am now checking the default-on feature behavior and the explicit DNS test feature set before renaming the spike.
Author
Owner

J254 resumed on mesh-dns-j254. Current branch head is c702c09c11a044be8dba2b139ef379edbfd16964; its current merge base is f9f8ced338278d924803a691f9636a9519babcb2, while fetched origin/dev is b2b15527f0c925f680d94c5e0deaa020ffdb0c56 (10 commits ahead of the task branch).

The existing uncommitted WIP renames the spike feature to mesh-dns across the mesh implementation and adjusts the privileged DNS target. It also adds a test asserting the shipping manifest enables mesh-dns; the manifest default still needs to change. I am preserving this patch and checking the J208b/J208c feature matrix against the current dev base. J78's punycode fix and the J254 D327 NXDOMAIN/SOA fix are already in the committed history. No resolver installation/removal changes are present yet.

Next: finish the feature/default and test-set slice, then implement resolver lifecycle on Linux and macOS, prove it on the named machines, measure latency/memory/size, and run quick, fast and privileged gates. The earlier quick gate passed on c702c09; it does not cover this WIP. Compute01 currently has all four slots busy, so I am checking dev-02 before queueing the next run.

J254 resumed on `mesh-dns-j254`. Current branch head is `c702c09c11a044be8dba2b139ef379edbfd16964`; its current merge base is `f9f8ced338278d924803a691f9636a9519babcb2`, while fetched `origin/dev` is `b2b15527f0c925f680d94c5e0deaa020ffdb0c56` (10 commits ahead of the task branch). The existing uncommitted WIP renames the spike feature to `mesh-dns` across the mesh implementation and adjusts the privileged DNS target. It also adds a test asserting the shipping manifest enables `mesh-dns`; the manifest default still needs to change. I am preserving this patch and checking the J208b/J208c feature matrix against the current dev base. J78's punycode fix and the J254 D327 NXDOMAIN/SOA fix are already in the committed history. No resolver installation/removal changes are present yet. Next: finish the feature/default and test-set slice, then implement resolver lifecycle on Linux and macOS, prove it on the named machines, measure latency/memory/size, and run quick, fast and privileged gates. The earlier quick gate passed on `c702c09`; it does not cover this WIP. Compute01 currently has all four slots busy, so I am checking dev-02 before queueing the next run.
Author
Owner

J254 feature/build-matrix finding (base branch tree c702c09c1, current dev-02 feature probe):

  • crates/plugins/mesh/Cargo.toml still defaults to server; it does not enable mesh-dns. The previously added mesh-dns manifest assertion fails with AssertionError: 'mesh-dns' not found in ['server'] (mesh-dns-feature.red.log).
  • Both shipping role manifests select infraternal-plugin-mesh with default-features = false, but their mesh features only activate the dependency. A new dev-02 regression failed because infraternal-agent's mesh feature lacked infraternal-plugin-mesh/mesh-dns.
  • The privileged mesh test matrix passes --features agent,server,relay,test-support without --no-default-features; a new regression failed with the generated argv ['-p', 'infraternal-plugin-mesh', '--features', 'agent,server,relay,test-support', '--tests', '--examples']. Enabling the shipping default without fixing this would make the shared namespace fixture build inherit DNS-specific fields instead of keeping DNS isolated to test:dns.

I will make mesh-dns the package default, explicitly forward it through the shipping agent/server mesh roles, and make the privileged mesh matrix compile its shared and DNS targets with no implicit defaults, with DNS named only on the DNS target. The package-level mesh-dns feature will remain role-neutral so a server build does not pull in the agent engine just to carry DNS fields.

J254 feature/build-matrix finding (base branch tree `c702c09c1`, current dev-02 feature probe): - `crates/plugins/mesh/Cargo.toml` still defaults to `server`; it does not enable `mesh-dns`. The previously added `mesh-dns` manifest assertion fails with `AssertionError: 'mesh-dns' not found in ['server']` (`mesh-dns-feature.red.log`). - Both shipping role manifests select `infraternal-plugin-mesh` with `default-features = false`, but their `mesh` features only activate the dependency. A new dev-02 regression failed because `infraternal-agent`'s `mesh` feature lacked `infraternal-plugin-mesh/mesh-dns`. - The privileged mesh test matrix passes `--features agent,server,relay,test-support` without `--no-default-features`; a new regression failed with the generated argv `['-p', 'infraternal-plugin-mesh', '--features', 'agent,server,relay,test-support', '--tests', '--examples']`. Enabling the shipping default without fixing this would make the shared namespace fixture build inherit DNS-specific fields instead of keeping DNS isolated to `test:dns`. I will make `mesh-dns` the package default, explicitly forward it through the shipping agent/server mesh roles, and make the privileged mesh matrix compile its shared and DNS targets with no implicit defaults, with DNS named only on the DNS target. The package-level `mesh-dns` feature will remain role-neutral so a server build does not pull in the agent engine just to carry DNS fields.
Author
Owner

J254 progress: default-on mesh-dns and the explicit privileged DNS feature set are committed and pushed on mesh-dns-j254 at cea623f67. The three focused Python regressions passed on dev-02 (package default, shipping feature forwarding, and privileged build matrix). Branch base was f9f8ced33; origin/dev is now 3e659eb78. I am rebasing onto current dev, then running verify quick before taking the resolver lifecycle work.

J254 progress: default-on mesh-dns and the explicit privileged DNS feature set are committed and pushed on mesh-dns-j254 at cea623f67. The three focused Python regressions passed on dev-02 (package default, shipping feature forwarding, and privileged build matrix). Branch base was f9f8ced33; origin/dev is now 3e659eb78. I am rebasing onto current dev, then running verify quick before taking the resolver lifecycle work.
Author
Owner

J254 gate finding: after rebasing onto 3e659eb78, verify quick exited 1 at doc comments on diff; all earlier quick rows passed. The D345 checker flags mesh functions and test helpers whose bodies/config gates changed as mesh-dns-spike was renamed to mesh-dns. Full findings are in /home/claude/.cache/codex-scratch/j254/evidence/verify-quick.log on compute01. I am adding the required comments to those touched items and will rerun quick.

J254 gate finding: after rebasing onto 3e659eb78, verify quick exited 1 at `doc comments on diff`; all earlier quick rows passed. The D345 checker flags mesh functions and test helpers whose bodies/config gates changed as `mesh-dns-spike` was renamed to `mesh-dns`. Full findings are in `/home/claude/.cache/codex-scratch/j254/evidence/verify-quick.log` on compute01. I am adding the required comments to those touched items and will rerun quick.
Author
Owner

J254 resumed at head 0ade90a1. Branch base is 3e659eb7; origin/dev is 159edcedc. The D327 response fix and default-on mesh-dns are committed, but resolver install/removal is still absent on both operating systems. The queued quick-gate process no longer appears in build-slot status; its final log is not yet readable, so I am not treating it as a pass. I will integrate current origin/dev, implement Linux and macOS resolver lifecycle, then prove lookup and cleanup on the named machines, measure latency and memory, and run quick, fast, and privileged on a free slot.

J254 resumed at head 0ade90a1. Branch base is 3e659eb7; origin/dev is 159edcedc. The D327 response fix and default-on mesh-dns are committed, but resolver install/removal is still absent on both operating systems. The queued quick-gate process no longer appears in build-slot status; its final log is not yet readable, so I am not treating it as a pass. I will integrate current origin/dev, implement Linux and macOS resolver lifecycle, then prove lookup and cleanup on the named machines, measure latency and memory, and run quick, fast, and privileged on a free slot.
Author
Owner

J254 resumed on mesh-dns-j254. Base: origin/dev at 159edcedc9978f0370ea8cb02a937082d631c9b6; branch head: 16005d27b. The existing pushed commits contain the D327 hidden-name NXDOMAIN fix and default-on mesh-dns. The only current worktree changes are the new resolver integration regression and its module declaration.

The focused resolved_route_is_per_link_and_covers_default_and_custom_suffixes test is queued through dev-02 build-slot at position 4. I am keeping implementation changes behind its red result. Evidence log: /home/claude/.cache/codex-scratch/j254/evidence/resolver-red.log.

Next: complete the D327/D339 audit; implement and test Linux systemd-resolved routing plus a narrow no-resolved fallback, and macOS resolver lifecycle; prove lookup and cleanup on compute01, dev-02, and the Mac VM; measure p50/p99 and memory per 1,000 names plus binary size; run verify quick, verify fast, and the privileged row. Estimate: 6–8 hours of VM work, with build-slot availability the main variable. I will not merge.

The live #266 issue endpoint currently reports 180 comments (the handoff said 154); I am continuing from the current issue history.

J254 resumed on `mesh-dns-j254`. Base: `origin/dev` at `159edcedc9978f0370ea8cb02a937082d631c9b6`; branch head: `16005d27b`. The existing pushed commits contain the D327 hidden-name NXDOMAIN fix and default-on `mesh-dns`. The only current worktree changes are the new resolver integration regression and its module declaration. The focused `resolved_route_is_per_link_and_covers_default_and_custom_suffixes` test is queued through dev-02 `build-slot` at position 4. I am keeping implementation changes behind its red result. Evidence log: `/home/claude/.cache/codex-scratch/j254/evidence/resolver-red.log`. Next: complete the D327/D339 audit; implement and test Linux systemd-resolved routing plus a narrow no-resolved fallback, and macOS resolver lifecycle; prove lookup and cleanup on compute01, dev-02, and the Mac VM; measure p50/p99 and memory per 1,000 names plus binary size; run `verify quick`, `verify fast`, and the privileged row. Estimate: 6–8 hours of VM work, with build-slot availability the main variable. I will not merge. The live #266 issue endpoint currently reports 180 comments (the handoff said 154); I am continuing from the current issue history.
Author
Owner

J254 progress: the focused resolver integration regression is still queued on dev-02 build-slot at position 4, with both slots occupied. I am waiting on its foreground run before adding resolver behavior. Read-only review confirms the existing directional name filtering, placeholder omission, custom-suffix precedence and J78 ASCII punycode fix remain intact; no decision changes are planned. The OS integration work will route only the mesh suffix, use managed hosts entries when resolved is absent, and leave macOS system resolver files untouched for a rootless node.

J254 progress: the focused resolver integration regression is still queued on dev-02 build-slot at position 4, with both slots occupied. I am waiting on its foreground run before adding resolver behavior. Read-only review confirms the existing directional name filtering, placeholder omission, custom-suffix precedence and J78 ASCII punycode fix remain intact; no decision changes are planned. The OS integration work will route only the mesh suffix, use managed hosts entries when resolved is absent, and leave macOS system resolver files untouched for a rootless node.
Author
Owner

J254 red-test result: the focused dev-02 lib test reached compilation and failed because the new dns_integration resolver helpers are absent (E0432, unresolved imports), before any production integration code existed. The run also exposed a test-only fixed-array-length mismatch in the expected systemd command list; I corrected the expectation to a vector of argument vectors and will rerun the corrected test. No production behavior has changed.

J254 red-test result: the focused dev-02 lib test reached compilation and failed because the new dns_integration resolver helpers are absent (`E0432`, unresolved imports), before any production integration code existed. The run also exposed a test-only fixed-array-length mismatch in the expected systemd command list; I corrected the expectation to a vector of argument vectors and will rerun the corrected test. No production behavior has changed.
Author
Owner

J254 progress (2026-11-06 05:38 IST): branch mesh-dns-j254, based at 159edcedc9. The focused resolved-route regression was corrected and synced to dev-02; its foreground build is still queued behind two busy slots (PID 2358645, queue position 4, free disk 214 GB). The source trace confirms Linux has the only running DNS listener, while macOS advertises dns_names=false and has no listener. I will share the listener and keep host integration split: systemd-resolved per-link plus the managed /etc/hosts fallback on Linux; managed /etc/resolver files only when macOS runs as root. A userspace Mac can serve peer lookups but cannot safely change the host system resolver. Next: implement helpers and lifecycle cleanup, then rerun the focused test, verify gates, real-machine proof, size and latency measurements.

J254 progress (2026-11-06 05:38 IST): branch mesh-dns-j254, based at 159edcedc9978f0370ea8cb02a937082d631c9b6. The focused resolved-route regression was corrected and synced to dev-02; its foreground build is still queued behind two busy slots (PID 2358645, queue position 4, free disk 214 GB). The source trace confirms Linux has the only running DNS listener, while macOS advertises dns_names=false and has no listener. I will share the listener and keep host integration split: systemd-resolved per-link plus the managed /etc/hosts fallback on Linux; managed /etc/resolver files only when macOS runs as root. A userspace Mac can serve peer lookups but cannot safely change the host system resolver. Next: implement helpers and lifecycle cleanup, then rerun the focused test, verify gates, real-machine proof, size and latency measurements.
Author
Owner

Correction to my 05:38 IST progress note: the earlier J254 #266 plan calls for a per-link NetworkManager fallback when systemd-resolved is unavailable. I will follow that trail and use /etc/hosts only if the installed NetworkManager mode cannot provide suffix-only routing without changing the host default resolver. The saved Mac evidence also confirms /etc/resolver uses the filename as the domain and that the test userspace account can use noninteractive sudo; I am checking the existing service privilege path before choosing how to apply that safely.

Correction to my 05:38 IST progress note: the earlier J254 #266 plan calls for a per-link NetworkManager fallback when systemd-resolved is unavailable. I will follow that trail and use /etc/hosts only if the installed NetworkManager mode cannot provide suffix-only routing without changing the host default resolver. The saved Mac evidence also confirms /etc/resolver uses the filename as the domain and that the test userspace account can use noninteractive sudo; I am checking the existing service privilege path before choosing how to apply that safely.
Author
Owner

J254 environment finding (2026-11-06 05:50 IST): dev-02 build-slot reports the corrected focused regression still queued (PID 2358645, position 3 after ~42 min; 225 GB free). Both named Linux VMs have systemd 257/255 and no nmcli/NetworkManager, so I can exercise the real systemd-resolved path there and cover the NetworkManager fallback with argument/cleanup unit tests; I will report that the fallback could not be proven on these machines. On macOS the service lives under /Library/LaunchDaemons without a UserName override, so the userspace tunnel can manage /etc/resolver from the root service. A truly user-only process will skip system file changes.

J254 environment finding (2026-11-06 05:50 IST): dev-02 build-slot reports the corrected focused regression still queued (PID 2358645, position 3 after ~42 min; 225 GB free). Both named Linux VMs have systemd 257/255 and no nmcli/NetworkManager, so I can exercise the real systemd-resolved path there and cover the NetworkManager fallback with argument/cleanup unit tests; I will report that the fallback could not be proven on these machines. On macOS the service lives under /Library/LaunchDaemons without a UserName override, so the userspace tunnel can manage /etc/resolver from the root service. A truly user-only process will skip system file changes.
Author
Owner

J254 red evidence: dev-02 foreground run cargo test --locked --jobs 3 -p infraternal-plugin-mesh --features agent,server,mesh-dns --lib dns_integration::tests::resolved_route_is_per_link_and_covers_default_and_custom_suffixes -- --exact --nocapture acquired build-slot 1/2 and exited 101. It failed at compile as expected: E0432 for macos_resolver_write_allowed, remove_hosts_block, render_macos_resolver, replace_hosts_block, and resolved_route_commands imported by the new regression module. No production resolver-integration code has run yet. Next I am implementing these tested contracts plus Linux/macOS setup and cleanup.

J254 red evidence: dev-02 foreground run cargo test --locked --jobs 3 -p infraternal-plugin-mesh --features agent,server,mesh-dns --lib dns_integration::tests::resolved_route_is_per_link_and_covers_default_and_custom_suffixes -- --exact --nocapture acquired build-slot 1/2 and exited 101. It failed at compile as expected: E0432 for macos_resolver_write_allowed, remove_hosts_block, render_macos_resolver, replace_hosts_block, and resolved_route_commands imported by the new regression module. No production resolver-integration code has run yet. Next I am implementing these tested contracts plus Linux/macOS setup and cleanup.
Author
Owner

J254 continuation update (2026-11-06 IST): the working branch is still based on 159edced while fetched origin/dev is 527726f, so I am rebasing the preserved resolver WIP before extending it. Audit finding: nmcli per-device DNS does not specify the listener port 1053, and an /etc/hosts-only fallback cannot safely route arbitrary custom-suffix negative lookups. I will use only a verified suffix-routing path (systemd-resolved or a working NetworkManager dnsmasq configuration); if neither is available, preserve the host resolver and report the limitation. The Mac LaunchDaemon runs as root with no UserName override, so its /etc/resolver lifecycle is viable; a direct rootless process will leave system resolver files alone. Next: finish lifecycle wiring and tests, then real-machine proof, measurements, and gates. Existing estimate remains 6–8 hours of VM work.

J254 continuation update (2026-11-06 IST): the working branch is still based on 159edced while fetched origin/dev is 527726f, so I am rebasing the preserved resolver WIP before extending it. Audit finding: nmcli per-device DNS does not specify the listener port 1053, and an /etc/hosts-only fallback cannot safely route arbitrary custom-suffix negative lookups. I will use only a verified suffix-routing path (systemd-resolved or a working NetworkManager dnsmasq configuration); if neither is available, preserve the host resolver and report the limitation. The Mac LaunchDaemon runs as root with no UserName override, so its /etc/resolver lifecycle is viable; a direct rootless process will leave system resolver files alone. Next: finish lifecycle wiring and tests, then real-machine proof, measurements, and gates. Existing estimate remains 6–8 hours of VM work.
Author
Owner

J254 rebase update: rebased and force-pushed the own branch onto current origin/dev 527726fbfc; branch head is 99551f10f8. The existing resolver test/accessor WIP is restored locally and remains uncommitted. Before implementation, I am replacing the unsafe/ineffective fallback design with a safe split-DNS path and explicit no-degradation behavior when the host cannot route the custom suffix.

J254 rebase update: rebased and force-pushed the own branch onto current origin/dev 527726fbfc9b41c714c2aa1b5687a438261954ba; branch head is 99551f10f82b5885083cf2e3b5b17811197b4b62. The existing resolver test/accessor WIP is restored locally and remains uncommitted. Before implementation, I am replacing the unsafe/ineffective fallback design with a safe split-DNS path and explicit no-degradation behavior when the host cannot route the custom suffix.
Author
Owner

J254 quick-gate finding at 99551f10f on compute01: scripts/verify quick reached workspace clippy and failed. WorkerCommand::Apply in Linux gotatun_node.rs now carries a 248-byte Config, crossing large_enum_variant after the DNS snapshot fields were enabled; the macOS worker has the same inline payload and needs the same indirection. The gate also found two missing GotaTun backticks in D345 comments (gotatun_node.rs and node_actions.rs). I am fixing these directly related feature/build findings, then I will rerun quick.

J254 quick-gate finding at 99551f10f on compute01: `scripts/verify quick` reached workspace clippy and failed. `WorkerCommand::Apply` in Linux `gotatun_node.rs` now carries a 248-byte `Config`, crossing `large_enum_variant` after the DNS snapshot fields were enabled; the macOS worker has the same inline payload and needs the same indirection. The gate also found two missing `GotaTun` backticks in D345 comments (`gotatun_node.rs` and `node_actions.rs`). I am fixing these directly related feature/build findings, then I will rerun quick.
Author
Owner

J254 progress from head 99551f10f82b5885083cf2e3b5b17811197b4b62 (base origin/dev 527726fbfc9b41c714c2aa1b5687a438261954ba).

Finding: the resolver WIP's last-resort Linux mode writes known names into /etc/hosts. That does not route the whole custom suffix: unknown or hidden names still go to the host's ordinary DNS path, and a custom suffix can overlap a public zone. I am removing that fallback. The safe fallback is NetworkManager only when its dnsmasq plugin is active, it manages infraternal0, and this node has bound UDP 53; otherwise the host resolver stays as it is. The focused test already expects no global fallback when neither split-DNS path is available.

The resumed current-base verify quick stopped at doc comments on diff and named gotatun_node.rs::run_worker, macos_node.rs::Worker::apply, and macos_node.rs::run_worker; I added those comments locally. The full log remains at dev-02 ~/.cache/codex-scratch/j254/evidence/quick-99551-fix-dev02.log. Next I will sync the documentation fix, complete the focused listener and lifecycle regressions, and rerun quick once a slot is free. No host or mesh state has been changed.

J254 progress from head `99551f10f82b5885083cf2e3b5b17811197b4b62` (base `origin/dev` `527726fbfc9b41c714c2aa1b5687a438261954ba`). Finding: the resolver WIP's last-resort Linux mode writes known names into `/etc/hosts`. That does not route the whole custom suffix: unknown or hidden names still go to the host's ordinary DNS path, and a custom suffix can overlap a public zone. I am removing that fallback. The safe fallback is NetworkManager only when its dnsmasq plugin is active, it manages `infraternal0`, and this node has bound UDP 53; otherwise the host resolver stays as it is. The focused test already expects no global fallback when neither split-DNS path is available. The resumed current-base `verify quick` stopped at `doc comments on diff` and named `gotatun_node.rs::run_worker`, `macos_node.rs::Worker::apply`, and `macos_node.rs::run_worker`; I added those comments locally. The full log remains at dev-02 `~/.cache/codex-scratch/j254/evidence/quick-99551-fix-dev02.log`. Next I will sync the documentation fix, complete the focused listener and lifecycle regressions, and rerun quick once a slot is free. No host or mesh state has been changed.
Author
Owner

J254 audit result for D327, D339 and D352:

  • D327 is already covered by the pushed regression denied_remote_names_match_the_nonexistent_name_response: denied and nonexistent queries compare byte-identical DNS response sections, including the negative SOA. configuration::compile omits a peer name when outbound access is None; the resolver returns the common NXDOMAIN path before looking up records for a remote source. That keeps the remote denied and nonexistent queries in the same lookup/timing class.
  • D339 is already reflected in the snapshot path: store::wanted_name chooses the recorded/reported hostname or fallback_machine_name; configuration::compile publishes those member.node.host values, and the resolver inserts only configured names. Legacy unnamed-* labels are used by the store only to recognize old records during rename, not projected into DNS. No implementation gap found there.
  • D352 has a Mac gap: lib.rs compiles dns only for Linux, and macos_node::Node::up advertises dns_names: false. Mac nodes therefore do not receive peer-name snapshots or run the resolver. I will enable the shared Hickory listener on macOS, advertise DNS support when the feature is enabled, and wire the existing root-only /etc/resolver helper into accepted snapshots and stop. A rootless process will keep system resolver files untouched.

No numbered decision changes are needed.

J254 audit result for D327, D339 and D352: - D327 is already covered by the pushed regression `denied_remote_names_match_the_nonexistent_name_response`: denied and nonexistent queries compare byte-identical DNS response sections, including the negative SOA. `configuration::compile` omits a peer name when outbound access is `None`; the resolver returns the common NXDOMAIN path before looking up records for a remote source. That keeps the remote denied and nonexistent queries in the same lookup/timing class. - D339 is already reflected in the snapshot path: `store::wanted_name` chooses the recorded/reported hostname or `fallback_machine_name`; `configuration::compile` publishes those `member.node.host` values, and the resolver inserts only configured names. Legacy `unnamed-*` labels are used by the store only to recognize old records during rename, not projected into DNS. No implementation gap found there. - D352 has a Mac gap: `lib.rs` compiles `dns` only for Linux, and `macos_node::Node::up` advertises `dns_names: false`. Mac nodes therefore do not receive peer-name snapshots or run the resolver. I will enable the shared Hickory listener on macOS, advertise DNS support when the feature is enabled, and wire the existing root-only `/etc/resolver` helper into accepted snapshots and stop. A rootless process will keep system resolver files untouched. No numbered decision changes are needed.
Author
Owner

J254 progress on mesh-dns-j254 (base 527726fbfc9b41c714c2aa1b5687a438261954ba): after syncing the current worktree to compute01, scripts/verify quick reached the repo checks. The scope guard, migration names, Nix Cargo vendoring, fmt, doc coverage, shape lints, and fixed-test-waits rows passed. The first failing row was doc comments on diff; it reported nine changed declarations/functions in lib.rs, macos_node.rs, macos_service.rs, node.rs, and tests/dns.rs. I’m adding those docs before rerunning the precheck.

J254 progress on `mesh-dns-j254` (base `527726fbfc9b41c714c2aa1b5687a438261954ba`): after syncing the current worktree to compute01, `scripts/verify quick` reached the repo checks. The scope guard, migration names, Nix Cargo vendoring, fmt, doc coverage, shape lints, and fixed-test-waits rows passed. The first failing row was `doc comments on diff`; it reported nine changed declarations/functions in `lib.rs`, `macos_node.rs`, `macos_service.rs`, `node.rs`, and `tests/dns.rs`. I’m adding those docs before rerunning the precheck.
Author
Owner

J254 finding after the resolver lint fixes: scripts/verify quick passes fmt and the doc checks, then workspace clippy compiles all Mesh test targets with the default mesh-dns feature and fails on three fixtures. tests/gotatun_restore.rs refers to private dataplane::DEFAULT_DNS_SUFFIX; tests/proxy_ingress_namespace.rs and tests/ssh_original_port.rs omit the new Config DNS fields. I’m updating those fixtures with #[cfg(feature = "mesh-dns")] fields and the public default value internal, preserving their no-default-feature builds.

J254 finding after the resolver lint fixes: `scripts/verify quick` passes fmt and the doc checks, then workspace clippy compiles all Mesh test targets with the default `mesh-dns` feature and fails on three fixtures. `tests/gotatun_restore.rs` refers to private `dataplane::DEFAULT_DNS_SUFFIX`; `tests/proxy_ingress_namespace.rs` and `tests/ssh_original_port.rs` omit the new `Config` DNS fields. I’m updating those fixtures with `#[cfg(feature = "mesh-dns")]` fields and the public default value `internal`, preserving their no-default-feature builds.
Author
Owner

J254 follow-up: after the three mesh fixtures were made compatible with the default DNS feature, verify quick reached the D345 diff check and flagged their two modified configuration helpers (proxy_ingress_namespace::configuration_with and ssh_original_port::configuration). I added short docs to both helpers; the next precheck will cover them with workspace clippy and changed-package unit tests.

J254 follow-up: after the three mesh fixtures were made compatible with the default DNS feature, `verify quick` reached the D345 diff check and flagged their two modified configuration helpers (`proxy_ingress_namespace::configuration_with` and `ssh_original_port::configuration`). I added short docs to both helpers; the next precheck will cover them with workspace clippy and changed-package unit tests.
Author
Owner

J254 feature-matrix finding: the updated Mesh fixtures cleared the earlier clippy errors. verify quick then compiled tests/configuration.rs with mesh-dns and found two compile_with_dns_suffix calls (around lines 38 and 578) missing argument 6, the control-plane TrafficPolicy. I’m updating both fixtures to pass an explicit policy so the tests compile in the default feature set.

J254 feature-matrix finding: the updated Mesh fixtures cleared the earlier clippy errors. `verify quick` then compiled `tests/configuration.rs` with `mesh-dns` and found two `compile_with_dns_suffix` calls (around lines 38 and 578) missing argument 6, the control-plane `TrafficPolicy`. I’m updating both fixtures to pass an explicit policy so the tests compile in the default feature set.
Author
Owner

J254 follow-up: verify quick passed the DNS feature-matrix compilation failure after both policy arguments were added. The D345 diff check now flags the two modified integration test functions in tests/configuration.rs because their calls changed. I’m adding concise doc comments before rerunning quick.

J254 follow-up: `verify quick` passed the DNS feature-matrix compilation failure after both policy arguments were added. The D345 diff check now flags the two modified integration test functions in `tests/configuration.rs` because their calls changed. I’m adding concise doc comments before rerunning quick.
Author
Owner

J254 feature-matrix finding: verify quick passed the Mesh configuration tests' updated DNS arguments, then workspace clippy exposed two additional fixtures compiled with the infraternal-agent mesh feature. crates/infraternal-agent/src/plugins.rs:489 and remote_supervisor_review_tests.rs:317 initialize Mesh Config without name, dns_suffix, or peer_names. I’m adding fields gated on this crate's mesh feature.

J254 feature-matrix finding: `verify quick` passed the Mesh configuration tests' updated DNS arguments, then workspace clippy exposed two additional fixtures compiled with the `infraternal-agent` `mesh` feature. `crates/infraternal-agent/src/plugins.rs:489` and `remote_supervisor_review_tests.rs:317` initialize Mesh `Config` without `name`, `dns_suffix`, or `peer_names`. I’m adding fields gated on this crate's `mesh` feature.
Author
Owner

J254 D345 follow-up: the agent feature-gated DNS fields now compile. verify quick passed the prior doc checks and found one newly touched test in crates/infraternal-agent/src/plugins.rs without a doc comment (accepted_config_exports_only_inbound_tcp_sources_for_the_published_port). I’m documenting that test before the next run.

J254 D345 follow-up: the agent feature-gated DNS fields now compile. `verify quick` passed the prior doc checks and found one newly touched test in `crates/infraternal-agent/src/plugins.rs` without a doc comment (`accepted_config_exports_only_inbound_tcp_sources_for_the_published_port`). I’m documenting that test before the next run.
Author
Owner

J254 progress: fixed the last reported quick-gate finding by documenting the accepted exposure-policy test in crates/infraternal-agent/src/plugins.rs. Dev-02 has two active slots and a queued run from another worktree; compute01 currently has one free slot, so I am syncing this WIP there and rerunning verify quick. Branch: mesh-dns-j254; current head: 99551f10f8.

J254 progress: fixed the last reported quick-gate finding by documenting the accepted exposure-policy test in crates/infraternal-agent/src/plugins.rs. Dev-02 has two active slots and a queued run from another worktree; compute01 currently has one free slot, so I am syncing this WIP there and rerunning verify quick. Branch: mesh-dns-j254; current head: 99551f10f82b5885083cf2e3b5b17811197b4b62.
Author
Owner

J254 finding from compute01 verify quick (2026-11-06): the scope, fmt, doc coverage, shape lint, D345 doc-comment, and fixed-wait rows passed. Workspace clippy then rejected crates/plugins/mesh/tests/gotatun_restore.rs:238 because GotaTun in the new test doc comment needs backticks (clippy::doc_markdown). I am applying that one-line documentation correction and will rerun quick on compute01.

J254 finding from compute01 verify quick (2026-11-06): the scope, fmt, doc coverage, shape lint, D345 doc-comment, and fixed-wait rows passed. Workspace clippy then rejected crates/plugins/mesh/tests/gotatun_restore.rs:238 because GotaTun in the new test doc comment needs backticks (clippy::doc_markdown). I am applying that one-line documentation correction and will rerun quick on compute01.
Author
Owner

J254 finding from compute01 verify quick: the GotaTun doc-markdown issue is fixed and clippy advanced through the workspace, then compilation failed at crates/infraternal-server/src/remote_mesh_revocation_tests.rs:33 because its Identity test fixture lacks dns_names. The build exited 1 before changed-package tests. I am tracing the Identity contract and will add the fixture value consistent with no published DNS names.

J254 finding from compute01 verify quick: the GotaTun doc-markdown issue is fixed and clippy advanced through the workspace, then compilation failed at crates/infraternal-server/src/remote_mesh_revocation_tests.rs:33 because its Identity test fixture lacks dns_names. The build exited 1 before changed-package tests. I am tracing the Identity contract and will add the fixture value consistent with no published DNS names.
Author
Owner

J254 owner-order update (2026-11-06): the owner has frozen infraternal-dev-02-std and infraternal-macos-01-std, so I will not sync, queue builds, test or acquire the Mac lock there until the freeze is lifted. Mac resolver implementation verification and cleanup proof are pending. Two-VM Linux DNS/SSH and denied-name proof is also pending because the second VM is frozen. I am finishing and gating the Linux implementation on compute01; the first quick run uncovered one more server test Identity fixture, now set to dns_names=false because this revocation peer does not negotiate resolver support.

J254 owner-order update (2026-11-06): the owner has frozen infraternal-dev-02-std and infraternal-macos-01-std, so I will not sync, queue builds, test or acquire the Mac lock there until the freeze is lifted. Mac resolver implementation verification and cleanup proof are pending. Two-VM Linux DNS/SSH and denied-name proof is also pending because the second VM is frozen. I am finishing and gating the Linux implementation on compute01; the first quick run uncovered one more server test Identity fixture, now set to dns_names=false because this revocation peer does not negotiate resolver support.
Author
Owner

J254 progress: rebased the published branch onto current origin/dev at 2ca748b8c, preserving newer key-rotation and coordinator code, and pushed mesh-dns-j254 at 63d7cd05c. The resolver integration WIP is still uncommitted while I finish Linux verification on compute01. Cross-VM Linux proof (dev-02 frozen) and Mac resolver verification/cleanup proof remain pending until the owner lifts the VM freeze.

J254 progress: rebased the published branch onto current origin/dev at 2ca748b8c, preserving newer key-rotation and coordinator code, and pushed mesh-dns-j254 at 63d7cd05c. The resolver integration WIP is still uncommitted while I finish Linux verification on compute01. Cross-VM Linux proof (dev-02 frozen) and Mac resolver verification/cleanup proof remain pending until the owner lifts the VM freeze.
Author
Owner

J254 progress: committed and pushed resolver integration WIP at bce602381a on mesh-dns-j254. The code is synced to current origin/dev. It uses only per-link/per-suffix resolver state and keeps unsupported/rootless hosts unchanged. The Mac path is included as unverified WIP; Mac VM, dev-02 cross-VM proof, latency/load and memory evidence remain pending. Compute01 currently has all four build slots occupied and a queue; I will wait for a free slot before rerunning quick.

J254 progress: committed and pushed resolver integration WIP at bce602381a3f82f1e38c0e0199b30e06606c7736 on mesh-dns-j254. The code is synced to current origin/dev. It uses only per-link/per-suffix resolver state and keeps unsupported/rootless hosts unchanged. The Mac path is included as unverified WIP; Mac VM, dev-02 cross-VM proof, latency/load and memory evidence remain pending. Compute01 currently has all four build slots occupied and a queue; I will wait for a free slot before rerunning quick.
Author
Owner

J254 Linux environment finding (read-only, compute01): systemd-resolved is active and infraternal0 is up, but resolvectl status infraternal0 shows no per-link DNS server or domains. I will claim ~/mesh-vm.claim before changing this live interface. Compute01 currently has all four build slots occupied; no J254 run is queued.

J254 Linux environment finding (read-only, compute01): systemd-resolved is active and infraternal0 is up, but `resolvectl status infraternal0` shows no per-link DNS server or domains. I will claim `~/mesh-vm.claim` before changing this live interface. Compute01 currently has all four build slots occupied; no J254 run is queued.
Author
Owner

J254 gate status: verify quick is waiting foreground in compute01 build-slot at queue position 5 (four jobs ahead). The std VMs remain frozen, so I am staying on compute01 as ordered. No gate output has begun yet.

J254 gate status: verify quick is waiting foreground in compute01 build-slot at queue position 5 (four jobs ahead). The std VMs remain frozen, so I am staying on compute01 as ordered. No gate output has begun yet.
Author
Owner

J254 gate wait update: compute01 build-slot advanced the quick run from queue position 6 to 4; no gate output has started. The oldest active fast gate is at 56 minutes. Continuing the foreground wait on compute01 only.

J254 gate wait update: compute01 build-slot advanced the quick run from queue position 6 to 4; no gate output has started. The oldest active fast gate is at 56 minutes. Continuing the foreground wait on compute01 only.
Author
Owner

J254 gate wait: compute01 advanced the queued quick run to position 3; two jobs remain ahead. A slot opened and another queued quick gate took it. J254 remains foreground and has not started execution.

J254 gate wait: compute01 advanced the queued quick run to position 3; two jobs remain ahead. A slot opened and another queued quick gate took it. J254 remains foreground and has not started execution.
Author
Owner

J254 progress (2026-11-06)

Branch mesh-dns-j254, based on origin/dev at 2ca748b8c07a5836462b380ef4e340d829731ec5; current head bce602381a3f82f1e38c0e0199b30e06606c7736.

The owner has frozen infraternal-dev-02-std and infraternal-macos-01-std; I will run builds and tests on compute01 and start nothing on either frozen VM. Pending macOS work: verify root /etc/resolver/<suffix> behavior and cleanup, determine a safe userspace resolver option, and run the Mac name-resolution and cleanup proof. No Mac lock or sync will be started during the freeze.

Linux implementation and regression tests are in the branch. systemd-resolved is active on compute01; read-only resolvectl status infraternal0 currently reports no DNS server or routing domain. The live mesh claim belongs to J175, so I will wait for that claim to clear before changing compute01 mesh state. Cross-VM Linux proof with dev-02 is pending the VM freeze.

The foreground verify quick run is still executing on compute01. verify fast, the explicit privileged row, resolver live proof, and performance measurements remain pending.

J254 progress (2026-11-06) Branch `mesh-dns-j254`, based on `origin/dev` at `2ca748b8c07a5836462b380ef4e340d829731ec5`; current head `bce602381a3f82f1e38c0e0199b30e06606c7736`. The owner has frozen `infraternal-dev-02-std` and `infraternal-macos-01-std`; I will run builds and tests on compute01 and start nothing on either frozen VM. Pending macOS work: verify root `/etc/resolver/<suffix>` behavior and cleanup, determine a safe userspace resolver option, and run the Mac name-resolution and cleanup proof. No Mac lock or sync will be started during the freeze. Linux implementation and regression tests are in the branch. `systemd-resolved` is active on compute01; read-only `resolvectl status infraternal0` currently reports no DNS server or routing domain. The live mesh claim belongs to J175, so I will wait for that claim to clear before changing compute01 mesh state. Cross-VM Linux proof with dev-02 is pending the VM freeze. The foreground `verify quick` run is still executing on compute01. `verify fast`, the explicit privileged row, resolver live proof, and performance measurements remain pending.
Author
Owner

J254 progress (2026-11-06): scripts/verify quick completed on compute01 with exit 0. Summary from the run:

Rust gate Result
scope guard PASS
migration names PASS
Nix Cargo vendoring PASS
fmt PASS
doc coverage ratchet PASS
shape lints off (D351) PASS
doc comments on diff PASS
fixed test waits PASS
clippy (workspace) PASS
unit tests (changed) PASS
unit tests (changed, bin-only) PASS

QUICK pre-check passed. This is not the gate: run scripts/verify fast before reporting work done.

The measurement harness is staged outside the checkout and its release build is waiting in the compute01 build-slot queue. The final fast gate and its privileged integration row remain pending.

J254 progress (2026-11-06): `scripts/verify quick` completed on compute01 with exit 0. Summary from the run: | Rust gate | Result | | --- | --- | | scope guard | PASS | | migration names | PASS | | Nix Cargo vendoring | PASS | | fmt | PASS | | doc coverage ratchet | PASS | | shape lints off (D351) | PASS | | doc comments on diff | PASS | | fixed test waits | PASS | | clippy (workspace) | PASS | | unit tests (changed) | PASS | | unit tests (changed, bin-only) | PASS | QUICK pre-check passed. This is not the gate: run scripts/verify fast before reporting work done. The measurement harness is staged outside the checkout and its release build is waiting in the compute01 build-slot queue. The final fast gate and its privileged integration row remain pending.
Author
Owner

J254 D327/D339 audit evidence (2026-11-06)

  • dns::response_for_source checks the requester before consulting Records.names. Every in-zone query from a non-local source goes through negative_response; a missing local name uses that same builder. This keeps denied remote lookups in the same response and name-lookup timing class as absent names. tests/dns.rs::denied_remote_names_match_the_nonexistent_name_response checks NXDOMAIN, the SOA, and byte-identical response sections after removing the DNS question, which must echo the queried name. It also checks the allowed local query still answers.
  • Records::from_config only projects the node name and policy-filtered peer_names. The coordinator filters legacy DNS placeholders. D339 naming uses the reported hostname or its documented machine fallback; unnamed and unnamed-<n> are only recognized to replace old stored placeholders. The mesh tests cover placeholder replacement and prevent an old host daemon/IP record from leaving a UUID-derived DNS name.
  • Linux integration tests assert a per-link systemd-resolved server/domain with default-route DNS disabled, a guarded NetworkManager dnsmasq fallback, and cleanup scoped to infraternal0.

The tests are in the branch; the foreground quick pre-check passed. Live resolver proof still waits on J175's compute01 mesh claim, and the two-VM proof remains deferred during the dev-02 freeze.

J254 D327/D339 audit evidence (2026-11-06) - `dns::response_for_source` checks the requester before consulting `Records.names`. Every in-zone query from a non-local source goes through `negative_response`; a missing local name uses that same builder. This keeps denied remote lookups in the same response and name-lookup timing class as absent names. `tests/dns.rs::denied_remote_names_match_the_nonexistent_name_response` checks NXDOMAIN, the SOA, and byte-identical response sections after removing the DNS question, which must echo the queried name. It also checks the allowed local query still answers. - `Records::from_config` only projects the node name and policy-filtered `peer_names`. The coordinator filters legacy DNS placeholders. D339 naming uses the reported hostname or its documented machine fallback; `unnamed` and `unnamed-<n>` are only recognized to replace old stored placeholders. The mesh tests cover placeholder replacement and prevent an old host daemon/IP record from leaving a UUID-derived DNS name. - Linux integration tests assert a per-link systemd-resolved server/domain with default-route DNS disabled, a guarded NetworkManager dnsmasq fallback, and cleanup scoped to `infraternal0`. The tests are in the branch; the foreground quick pre-check passed. Live resolver proof still waits on J175's compute01 mesh claim, and the two-VM proof remains deferred during the dev-02 freeze.
Author
Owner

J254 progress (2026-11-06): the clean task checkout is at b125ab2600. After fetching origin, current dev is 21 commits ahead of the branch's base; the only overlapping path is a separate fixture change in remote_supervisor_review_tests.rs. I will rebase the J254 commits onto current origin/dev (without merging this branch into dev), then rerun quick and fast on compute01.

The common brief confirms infraternal-dev-02-std and infraternal-macos-01-std are frozen. No work has been started on either. The Mac resolver behavior/cleanup and rootless resolver proof, plus two-VM Linux DNS/SSH and denied-name proof, remain pending until the freeze is lifted. On compute01, the live mesh claim still belongs to J175, so resolver live proof waits for that claim to clear. The four build slots are currently occupied; the final gate and DNS performance measurements are pending.

J254 progress (2026-11-06): the clean task checkout is at b125ab2600995d926d2c7f472db4dd673fd28506. After fetching origin, current dev is 21 commits ahead of the branch's base; the only overlapping path is a separate fixture change in remote_supervisor_review_tests.rs. I will rebase the J254 commits onto current origin/dev (without merging this branch into dev), then rerun quick and fast on compute01. The common brief confirms infraternal-dev-02-std and infraternal-macos-01-std are frozen. No work has been started on either. The Mac resolver behavior/cleanup and rootless resolver proof, plus two-VM Linux DNS/SSH and denied-name proof, remain pending until the freeze is lifted. On compute01, the live mesh claim still belongs to J175, so resolver live proof waits for that claim to clear. The four build slots are currently occupied; the final gate and DNS performance measurements are pending.
Author
Owner

J254 update: rebased onto current origin/dev at 863c8f97fc and pushed mesh-dns-j254 at be5b4ea015. No conflict; the updated fixture change and D351 gate are included. I am starting the required quick precheck on compute01, through its build slot, using the checkout's existing target directory. The dev-02 and Mac freeze remains in force; cross-VM and Mac resolver proof stay pending.

J254 update: rebased onto current origin/dev at 863c8f97fc8d93002d3cba7590004fa61421a6c8 and pushed mesh-dns-j254 at be5b4ea015254d1451b94922deb521c6781b8834. No conflict; the updated fixture change and D351 gate are included. I am starting the required quick precheck on compute01, through its build slot, using the checkout's existing target directory. The dev-02 and Mac freeze remains in force; cross-VM and Mac resolver proof stay pending.
Author
Owner

J254 gate finding: the first post-rebase quick attempt acquired compute01 build slot 2/4, then stopped before running the gate because the bare SSH shell did not have cargo on PATH. The traceback ends in FileNotFoundError: [Errno 2] No such file or directory: 'cargo' from scripts/verify.d/changed-scope.py. Evidence: /home/claude/infraternal-ops/j254-evidence/verify-quick-rebase-env-fail.log. No code test ran or failed. I am rerunning quick through the required flake dev shell with nix develop -c.

J254 gate finding: the first post-rebase quick attempt acquired compute01 build slot 2/4, then stopped before running the gate because the bare SSH shell did not have cargo on PATH. The traceback ends in FileNotFoundError: [Errno 2] No such file or directory: 'cargo' from scripts/verify.d/changed-scope.py. Evidence: /home/claude/infraternal-ops/j254-evidence/verify-quick-rebase-env-fail.log. No code test ran or failed. I am rerunning quick through the required flake dev shell with nix develop -c.
Author
Owner

J254 gate update: after using the required Nix dev shell, verify quick passed on compute01 at rebased head be5b4ea015. Its final summary reports scope guard, Nix Cargo vendoring, fmt, doc coverage, D351 shape lints, doc comments, fixed waits, workspace clippy, and both changed-package test rows as PASS. Evidence: /home/claude/infraternal-ops/j254-evidence/verify-quick-rebase.log. I am starting verify fast through build-slot on the same checkout and target directory; the privileged integration row is part of this run.

J254 gate update: after using the required Nix dev shell, verify quick passed on compute01 at rebased head be5b4ea015254d1451b94922deb521c6781b8834. Its final summary reports scope guard, Nix Cargo vendoring, fmt, doc coverage, D351 shape lints, doc comments, fixed waits, workspace clippy, and both changed-package test rows as PASS. Evidence: /home/claude/infraternal-ops/j254-evidence/verify-quick-rebase.log. I am starting verify fast through build-slot on the same checkout and target directory; the privileged integration row is part of this run.
Author
Owner

J254 verify fast result (compute01; exit 0) at be5b4ea015. Verbatim final Rust gate table from the run:

+---------------------------+--------------+
| Rust gate | Result |
+---------------------------+--------------+
| Nix Cargo vendoring | PASS |
| fmt | PASS |
| migration names | PASS |
| migration tooling tests | PASS |
| VT native fmt | PASS |
| CLI plugin compile-out | PASS |
| clippy | PASS |
| SSH runtime test helper | PASS |
| real-binary fixture paths | PASS |
| Traefik component manifest | PASS |
| release component publication | PASS |
| test | PASS |
| SSH inode reuse fixture | PASS |
| shim process-per-user | PASS |
| cargo-deny | PASS |
| gate architecture | PASS |
| doc coverage ratchet | PASS |
| shape lints off (D351) | PASS |
| doc comments on diff | PASS |
| fixed test waits | PASS |
| privileged integration | PASS |
| probe release exclusion | PASS |
| nightly fuzz reporter | PASS |
| static builds | PASS |
+---------------------------+--------------+

The privileged matrix completed 96/96; the last line was 'privileged integration: PASS' and the row took 152s. Static sizes: x86_64 27,988,784 bytes vs 22,418,256 baseline (+5,570,528); aarch64 25,167,904 vs 20,453,280 (+4,714,624). Final log: /home/claude/infraternal-ops/j254-evidence/verify-fast-final.log. Quick also passed at this rebased head: /home/claude/infraternal-ops/j254-evidence/verify-quick-rebase.log.

Next: latency/RSS measurements on perf-01 through perf-run and one-node Linux resolver proof on compute01 after J175's live-mesh claim clears. dev-02 and Mac are frozen; two-VM Linux and Mac resolver proof remain pending.

J254 verify fast result (compute01; exit 0) at be5b4ea015254d1451b94922deb521c6781b8834. Verbatim final Rust gate table from the run: +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | Nix Cargo vendoring | PASS | | fmt | PASS | | migration names | PASS | | migration tooling tests | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | Traefik component manifest | PASS | | release component publication | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | doc coverage ratchet | PASS | | shape lints off (D351) | PASS | | doc comments on diff | PASS | | fixed test waits | PASS | | privileged integration | PASS | | probe release exclusion | PASS | | nightly fuzz reporter | PASS | | static builds | PASS | +---------------------------+--------------+ The privileged matrix completed 96/96; the last line was 'privileged integration: PASS' and the row took 152s. Static sizes: x86_64 27,988,784 bytes vs 22,418,256 baseline (+5,570,528); aarch64 25,167,904 vs 20,453,280 (+4,714,624). Final log: /home/claude/infraternal-ops/j254-evidence/verify-fast-final.log. Quick also passed at this rebased head: /home/claude/infraternal-ops/j254-evidence/verify-quick-rebase.log. Next: latency/RSS measurements on perf-01 through perf-run and one-node Linux resolver proof on compute01 after J175's live-mesh claim clears. dev-02 and Mac are frozen; two-VM Linux and Mac resolver proof remain pending.
Author
Owner

J254 performance progress: I staged a temporary DNS harness in the compute01 task checkout, outside the tracked diff. It starts the public UDP Resolver on loopback with 1,000 peer names, records warmed RSS before/after the snapshot update, and measures 80,000 sequential request/reply samples across 8 concurrent clients. It is being built for x86_64-musl through compute01's build-slot using the same workspace target. The binary will be copied to perf-01; measurements will run only through the installed /usr/local/bin/perf-run, which records before/after machine snapshots. Estimated remaining bench work: one short build and one perf run.

J254 performance progress: I staged a temporary DNS harness in the compute01 task checkout, outside the tracked diff. It starts the public UDP Resolver on loopback with 1,000 peer names, records warmed RSS before/after the snapshot update, and measures 80,000 sequential request/reply samples across 8 concurrent clients. It is being built for x86_64-musl through compute01's build-slot using the same workspace target. The binary will be copied to perf-01; measurements will run only through the installed /usr/local/bin/perf-run, which records before/after machine snapshots. Estimated remaining bench work: one short build and one perf run.
Author
Owner

J254 performance harness finding: its first musl build failed because the public dns module is gated behind the crate's agent feature, while the standalone example inherited default server+mesh-dns features only. Evidence: /home/claude/infraternal-ops/j254-evidence/dns-bench-build.log; compiler reported unresolved import infraternal_plugin_mesh::dns and noted the item is configured behind agent. This is a harness invocation issue, not a source/test failure. I will rebuild the same example with --features agent.

J254 performance harness finding: its first musl build failed because the public dns module is gated behind the crate's agent feature, while the standalone example inherited default server+mesh-dns features only. Evidence: /home/claude/infraternal-ops/j254-evidence/dns-bench-build.log; compiler reported unresolved import infraternal_plugin_mesh::dns and noted the item is configured behind agent. This is a harness invocation issue, not a source/test failure. I will rebuild the same example with --features agent.
Author
Owner

J254 perf transfer note: the static benchmark binary built successfully on compute01 (841 KiB, SHA-256 fcd04c2dafe4f97bf3cf80d3c9890e2b2e87cf56ec11cb8b7fc67ff60b5322dd), but compute01 cannot resolve the perf VM hostname for direct scp. I will relay only this binary through the control host, then verify the hash on perf-01 before running it under perf-run.

J254 perf transfer note: the static benchmark binary built successfully on compute01 (841 KiB, SHA-256 fcd04c2dafe4f97bf3cf80d3c9890e2b2e87cf56ec11cb8b7fc67ff60b5322dd), but compute01 cannot resolve the perf VM hostname for direct scp. I will relay only this binary through the control host, then verify the hash on perf-01 before running it under perf-run.
Author
Owner

J254 performance update (2026-11-06): the static x86_64 benchmark ran through the pinned perf VM's perf-run wrapper after its SHA-256 was verified. With 1,000 names and 8 concurrent UDP clients, 80,000 queries measured p50 75 us, p99 90 us, and 105,328.7 qps. Warmed resolver RSS changed from 712 KiB to 844 KiB after publishing the 1,000-name snapshot: +132 KiB / 135,168 bytes per 1,000 names. The perf wrapper recorded quiet before/after snapshots and 0 steal jiffies during the 1.299 s run. Evidence: /home/claude/infraternal-ops/j254-evidence/perf-run-20261106T100008-2372.{before,after}.txt on perf-01.

Pending proof:

  • Linux on compute01: per-link split-DNS resolution and clean removal; waiting for J175's live mesh claim at ~/mesh-vm.claim to clear before touching shared mesh or resolver state.
  • Two-VM Linux proof (ping <name>.internal, ssh <name>, and denied-name response matching an absent name): dev-02 remains frozen.
  • macOS proof: root /etc/resolver/<suffix> install/removal, determine the safe rootless/userspace option, and resolution/removal checks; the Mac remains frozen. No sync, test, build-slot run or Mac lock is being started there.
J254 performance update (2026-11-06): the static x86_64 benchmark ran through the pinned perf VM's `perf-run` wrapper after its SHA-256 was verified. With 1,000 names and 8 concurrent UDP clients, 80,000 queries measured p50 75 us, p99 90 us, and 105,328.7 qps. Warmed resolver RSS changed from 712 KiB to 844 KiB after publishing the 1,000-name snapshot: +132 KiB / 135,168 bytes per 1,000 names. The perf wrapper recorded quiet before/after snapshots and 0 steal jiffies during the 1.299 s run. Evidence: `/home/claude/infraternal-ops/j254-evidence/perf-run-20261106T100008-2372.{before,after}.txt` on perf-01. Pending proof: - Linux on compute01: per-link split-DNS resolution and clean removal; waiting for J175's live mesh claim at `~/mesh-vm.claim` to clear before touching shared mesh or resolver state. - Two-VM Linux proof (`ping <name>.internal`, `ssh <name>`, and denied-name response matching an absent name): dev-02 remains frozen. - macOS proof: root `/etc/resolver/<suffix>` install/removal, determine the safe rootless/userspace option, and resolution/removal checks; the Mac remains frozen. No sync, test, build-slot run or Mac lock is being started there.
Author
Owner

J254 update: the owner lifted the std VM freeze at 14:36 IST. I am resuming the Mac resolver verification on infraternal-macos-01-std under lockf -k /tmp/mac-bench.lock, with CARGO_INCREMENTAL=0 and one J254 target that will be removed before releasing the lock. The Mac has about 23 GB free, so I am keeping the compile to the mesh DNS Mac tests. The compute01 per-link resolver proof and the two-VM Linux proof remain pending; at the latest check the live mesh claim still belongs to J175.

J254 update: the owner lifted the std VM freeze at 14:36 IST. I am resuming the Mac resolver verification on `infraternal-macos-01-std` under `lockf -k /tmp/mac-bench.lock`, with `CARGO_INCREMENTAL=0` and one J254 target that will be removed before releasing the lock. The Mac has about 23 GB free, so I am keeping the compile to the mesh DNS Mac tests. The compute01 per-link resolver proof and the two-VM Linux proof remain pending; at the latest check the live mesh claim still belongs to J175.
Author
Owner

J254 Mac build finding: at branch head be5b4ea015254d1451b94922deb521c6781b8834, a native macOS test build with --no-default-features --features agent,mesh-dns failed before tests ran. Rust 1.98.1 reported E0658 at macos_node.rs:1229 (a feature cfg applied directly to expression statements) and E0425 at dns_integration.rs:75 (NetworkManagerSupport's impl was compiled on macOS although the type is Linux-only). The Mac lock remained held through cleanup; the per-job target was removed before release and no resolver files were changed. I have isolated fixes in J254's compute01 worktree and am rerunning the Mac build and live resolver checks before committing.

J254 Mac build finding: at branch head `be5b4ea015254d1451b94922deb521c6781b8834`, a native macOS test build with `--no-default-features --features agent,mesh-dns` failed before tests ran. Rust 1.98.1 reported E0658 at `macos_node.rs:1229` (a feature `cfg` applied directly to expression statements) and E0425 at `dns_integration.rs:75` (`NetworkManagerSupport`'s impl was compiled on macOS although the type is Linux-only). The Mac lock remained held through cleanup; the per-job target was removed before release and no resolver files were changed. I have isolated fixes in J254's compute01 worktree and am rerunning the Mac build and live resolver checks before committing.
Author
Owner

J254 Mac test target finding: after the resolver library compiled, Cargo still failed while compiling tests/access_updates.rs, which is Linux-only (wireguard and firewall APIs) but was gated only by feature = "agent". The same build showed Resolver::address() was Linux-only and unused on macOS. I changed the test crate guard to Linux + agent and cfg-gated the accessor. The next Mac run will compile the full package test targets, run the Mac DNS unit tests, and execute the temporary root and userspace resolver smoke checks.

J254 Mac test target finding: after the resolver library compiled, Cargo still failed while compiling `tests/access_updates.rs`, which is Linux-only (`wireguard` and `firewall` APIs) but was gated only by `feature = "agent"`. The same build showed `Resolver::address()` was Linux-only and unused on macOS. I changed the test crate guard to Linux + agent and cfg-gated the accessor. The next Mac run will compile the full package test targets, run the Mac DNS unit tests, and execute the temporary root and userspace resolver smoke checks.
Author
Owner

J254 Mac resolver finding (2026-11-06): the latest locked Mac build compiled the mesh package and its integration-test targets; all 3 DNS unit tests passed and the userspace smoke passed without modifying /etc/resolver. The root smoke installed /etc/resolver/internal with the expected 127.0.0.1, port 1053, and search domain, then the OS lookup of the test mesh name failed with nodename nor servname provided, or not known.

Evidence: /Users/lume/infraternal-ops/j254-evidence/mac-dns-resolver.log. Cleanup removed the temporary resolver route and target before unlock; the Mac had 22 GiB free afterward. Current clean state has no /etc/resolver directory or internal resolver entry.

Next: reproduce once under the Mac lock with scutil --dns and a direct dig @127.0.0.1 -p 1053 <test-name>.internal while the listener and resolver file are active. Then add a regression check and make the smallest fix only if evidence localizes the fault to this code. Estimate: one diagnostic rebuild/run plus one focused fix and verification, about 30 minutes. Linux real-environment proof remains pending; the current ~/mesh-vm.claim belongs to J259, so shared mesh state stays untouched.

J254 Mac resolver finding (2026-11-06): the latest locked Mac build compiled the mesh package and its integration-test targets; all 3 DNS unit tests passed and the userspace smoke passed without modifying `/etc/resolver`. The root smoke installed `/etc/resolver/internal` with the expected `127.0.0.1`, port `1053`, and search domain, then the OS lookup of the test mesh name failed with `nodename nor servname provided, or not known`. Evidence: `/Users/lume/infraternal-ops/j254-evidence/mac-dns-resolver.log`. Cleanup removed the temporary resolver route and target before unlock; the Mac had 22 GiB free afterward. Current clean state has no `/etc/resolver` directory or `internal` resolver entry. Next: reproduce once under the Mac lock with `scutil --dns` and a direct `dig @127.0.0.1 -p 1053 <test-name>.internal` while the listener and resolver file are active. Then add a regression check and make the smallest fix only if evidence localizes the fault to this code. Estimate: one diagnostic rebuild/run plus one focused fix and verification, about 30 minutes. Linux real-environment proof remains pending; the current `~/mesh-vm.claim` belongs to J259, so shared mesh state stays untouched.
Author
Owner

J254 Mac resolver root cause (2026-11-06): /etc/resolver changes are not picked up just from an atomic rename or direct file creation on this VM. The staging-in-directory approach exposed .internal.infraternal-<pid>-<n>.tmp as a resolver; moving staging to /etc removed that bad route, but scutil --dns still showed no mesh suffix route. A controlled check showed /usr/bin/killall -HUP mDNSResponder makes the final domain internal entry appear with 127.0.0.1:1053, and the same refresh after file removal clears it.

Evidence: /Users/lume/infraternal-ops/j254-evidence/mac-resolver-reload.log. The root live smoke with staging moved outside /etc/resolver failed its exact-route assertion before the refresh was added; its target was removed before unlocking. I am adding the tested refresh after install and removal, then rerunning root and userspace smokes for both internal and a custom suffix under the Mac lock.

J254 Mac resolver root cause (2026-11-06): `/etc/resolver` changes are not picked up just from an atomic rename or direct file creation on this VM. The staging-in-directory approach exposed `.internal.infraternal-<pid>-<n>.tmp` as a resolver; moving staging to `/etc` removed that bad route, but `scutil --dns` still showed no mesh suffix route. A controlled check showed `/usr/bin/killall -HUP mDNSResponder` makes the final `domain internal` entry appear with `127.0.0.1:1053`, and the same refresh after file removal clears it. Evidence: `/Users/lume/infraternal-ops/j254-evidence/mac-resolver-reload.log`. The root live smoke with staging moved outside `/etc/resolver` failed its exact-route assertion before the refresh was added; its target was removed before unlocking. I am adding the tested refresh after install and removal, then rerunning root and userspace smokes for both `internal` and a custom suffix under the Mac lock.
Author
Owner

J254 Mac resolver verification (2026-11-06): under lockf -k /tmp/mac-bench.lock with CARGO_INCREMENTAL=0, the mesh package and all its test targets compiled on the Mac. DNS integration tests passed 4/4 (2 live cases are ignored by the regular run). The userspace live smoke passed and left /etc/resolver unchanged. The root smoke installed both /etc/resolver/internal and /etc/resolver/mesh.j254.test; scutil --dns showed each routed to 127.0.0.1:1053, direct dig returned the loopback A record for both suffixes, and system ToSocketAddrs lookups returned the mesh address. Stop removed both files and the active resolver entries.

Evidence: /Users/lume/infraternal-ops/j254-evidence/mac-dns-resolver.log. Cleanup removed the test clone and target before unlock; the resolver directory and both files are absent, and the Mac has 26 GiB free. The rootless Mac path remains userspace DNS service without changing the host resolver. Compute01 quick/fast gates remain to run against these changes.

J254 Mac resolver verification (2026-11-06): under `lockf -k /tmp/mac-bench.lock` with `CARGO_INCREMENTAL=0`, the mesh package and all its test targets compiled on the Mac. DNS integration tests passed 4/4 (2 live cases are ignored by the regular run). The userspace live smoke passed and left `/etc/resolver` unchanged. The root smoke installed both `/etc/resolver/internal` and `/etc/resolver/mesh.j254.test`; `scutil --dns` showed each routed to `127.0.0.1:1053`, direct `dig` returned the loopback A record for both suffixes, and system `ToSocketAddrs` lookups returned the mesh address. Stop removed both files and the active resolver entries. Evidence: `/Users/lume/infraternal-ops/j254-evidence/mac-dns-resolver.log`. Cleanup removed the test clone and target before unlock; the resolver directory and both files are absent, and the Mac has 26 GiB free. The rootless Mac path remains userspace DNS service without changing the host resolver. Compute01 quick/fast gates remain to run against these changes.
Author
Owner

J254 final update for #266.

Branch mesh-dns-j254 is pushed at 2be58ce2c1777d9fea0d7190fbc8f3170420ff7d (parent be5b4ea015). The checkout is clean.

The Mac resolver work is complete. Under the required lockf -k /tmp/mac-bench.lock and CARGO_INCREMENTAL=0, system resolver lookups and direct dig succeeded for internal and mesh.j254.test; stopping the root node removed both resolver routes. The userspace smoke left /etc/resolver unchanged. The Mac target was deleted before unlocking. Detailed Mac evidence is in the preceding comments.

Previously recorded DNS load evidence: p50 75 µs, p99 90 µs, 105,328.7 queries/s, and 135,168 bytes per 1,000 names. The fast gate also built the full static artifacts: x86_64 27,988,784 bytes (gate baseline delta +5,570,528) and aarch64 25,167,904 bytes (delta +4,714,624). Those are report-only full-artifact deltas, not an isolated mesh-DNS feature delta; aarch64 key-memory execution was skipped on the x86_64 host.

Linux live resolver and two-VM ping/ssh proof remain outstanding in this handoff. The attached log is the full compute01 fast-gate run (nix develop -c scripts/verify fast through build-slot), which exited 0. Verbatim Rust summary:

+---------------------------+--------------+
| Rust gate                 | Result       |
+---------------------------+--------------+
| Nix Cargo vendoring       | PASS         |
| fmt                       | PASS         |
| migration names           | PASS         |
| migration tooling tests   | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| real-binary fixture paths | PASS         |
| Traefik component manifest | PASS         |
| release component publication | PASS     |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| doc coverage ratchet      | PASS         |
| shape lints off (D351)    | PASS         |
| doc comments on diff      | PASS         |
| fixed test waits          | PASS         |
| privileged integration    | PASS         |
| probe release exclusion  | PASS         |
| nightly fuzz reporter     | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+

Final TypeScript protocol output:

 50 pass
 0 fail
 77253 expect() calls
Ran 50 tests across 13 files. [13.83s]

The fast-gate fuzz reporter filed wire_frame_decode inputs to #345 and #346; its row returned OK.

J254 final update for #266. Branch `mesh-dns-j254` is pushed at `2be58ce2c1777d9fea0d7190fbc8f3170420ff7d` (parent `be5b4ea015`). The checkout is clean. The Mac resolver work is complete. Under the required `lockf -k /tmp/mac-bench.lock` and `CARGO_INCREMENTAL=0`, system resolver lookups and direct `dig` succeeded for `internal` and `mesh.j254.test`; stopping the root node removed both resolver routes. The userspace smoke left `/etc/resolver` unchanged. The Mac target was deleted before unlocking. Detailed Mac evidence is in the preceding comments. Previously recorded DNS load evidence: p50 75 µs, p99 90 µs, 105,328.7 queries/s, and 135,168 bytes per 1,000 names. The fast gate also built the full static artifacts: x86_64 27,988,784 bytes (gate baseline delta +5,570,528) and aarch64 25,167,904 bytes (delta +4,714,624). Those are report-only full-artifact deltas, not an isolated mesh-DNS feature delta; aarch64 key-memory execution was skipped on the x86_64 host. Linux live resolver and two-VM `ping`/`ssh` proof remain outstanding in this handoff. The attached log is the full compute01 fast-gate run (`nix develop -c scripts/verify fast` through `build-slot`), which exited 0. Verbatim Rust summary: ``` +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | Nix Cargo vendoring | PASS | | fmt | PASS | | migration names | PASS | | migration tooling tests | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | Traefik component manifest | PASS | | release component publication | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | doc coverage ratchet | PASS | | shape lints off (D351) | PASS | | doc comments on diff | PASS | | fixed test waits | PASS | | privileged integration | PASS | | probe release exclusion | PASS | | nightly fuzz reporter | PASS | | static builds | PASS | +---------------------------+--------------+ ``` Final TypeScript protocol output: ``` 50 pass 0 fail 77253 expect() calls Ran 50 tests across 13 files. [13.83s] ``` The fast-gate fuzz reporter filed `wire_frame_decode` inputs to #345 and #346; its row returned `OK`.
Author
Owner

Size context for the final update: the existing isolated candidate measurement in comment #71293 used hickory-proto 0.26.3, matching this branch. The parser/encoder harness measured 759,192 static bytes versus 414,328 baseline (+344,864 bytes), and 664 KiB warmed RSS at 1,000 records. This is the isolated library/parser harness cost, not a full mesh-DNS feature-only delta. The full shipped artifact sizes and gate-baseline deltas are in comment #82139.

Size context for the final update: the existing isolated candidate measurement in comment #71293 used `hickory-proto` 0.26.3, matching this branch. The parser/encoder harness measured 759,192 static bytes versus 414,328 baseline (+344,864 bytes), and 664 KiB warmed RSS at 1,000 records. This is the isolated library/parser harness cost, not a full mesh-DNS feature-only delta. The full shipped artifact sizes and gate-baseline deltas are in comment #82139.
Author
Owner

J254 Linux proof resumed on mesh-dns-j254 at 2be58ce2c1 (base be5b4ea015). Mac proof and fast gate are already recorded above. Both Linux hosts have systemd-resolved active and no infraternal0; compute01 also has an existing infraternal process and DNS service, which I will leave untouched. The J254 live-mesh claim is still present. I am preparing a throwaway production-agent proof with per-link split DNS, then will verify allowed lookup/ping/SSH, denied-name equality, and clean stop.

J254 Linux proof resumed on mesh-dns-j254 at 2be58ce2c1777d9fea0d7190fbc8f3170420ff7d (base be5b4ea015254d1451b94922deb521c6781b8834). Mac proof and fast gate are already recorded above. Both Linux hosts have systemd-resolved active and no infraternal0; compute01 also has an existing infraternal process and DNS service, which I will leave untouched. The J254 live-mesh claim is still present. I am preparing a throwaway production-agent proof with per-link split DNS, then will verify allowed lookup/ping/SSH, denied-name equality, and clean stop.
Author
Owner

Mac resolver work resumed at b125ab2600. Read-only VM state: no /etc/resolver directory or loaded system Mesh service; existing resolver routes are unchanged. The code uses root-owned per-suffix resolver files for the root node and leaves system DNS untouched for uid 501 userspace nodes. The SSH login cannot use sudo non-interactively; I am validating the userspace path and locked Mac build/tests before the root-node proof.

Mac resolver work resumed at b125ab2600995d926d2c7f472db4dd673fd28506. Read-only VM state: no /etc/resolver directory or loaded system Mesh service; existing resolver routes are unchanged. The code uses root-owned per-suffix resolver files for the root node and leaves system DNS untouched for uid 501 userspace nodes. The SSH login cannot use sudo non-interactively; I am validating the userspace path and locked Mac build/tests before the root-node proof.
Author
Owner

Correction to my previous progress note: git ls-remote fjweb refs/heads/mesh-dns-j254 confirms the pushed branch is 2be58ce2c1 (parent be5b4ea015). The local linked worktree is at b125ab2600 and diverges from that ref (7 local commits, 29 pushed commits). I am preserving the local ref and checking out the pushed head detached for the Mac re-verification. The VM is stopped and clean of resolver files; I will report the root proof only after repeating it on this head.

Correction to my previous progress note: `git ls-remote fjweb refs/heads/mesh-dns-j254` confirms the pushed branch is 2be58ce2c1777d9fea0d7190fbc8f3170420ff7d (parent be5b4ea015254d1451b94922deb521c6781b8834). The local linked worktree is at b125ab2600995d926d2c7f472db4dd673fd28506 and diverges from that ref (7 local commits, 29 pushed commits). I am preserving the local ref and checking out the pushed head detached for the Mac re-verification. The VM is stopped and clean of resolver files; I will report the root proof only after repeating it on this head.
Author
Owner

Linux J254 cleanup finding: both owned node sessions 7313 (compute01) and 20780 (dev-02) accepted {"command":"quit"} and exited cleanly. Afterward, ip link show dev infraternal0 reported “does not exist” on both hosts and resolvectl status infraternal0 reported “No such device”; only the existing eth0/enp0s3/tailscale0/wt0 links and their vpn.kayg.org/reverse domains remain. The J254 claim is being released after this cleanup. Directional check on dev-02: its allowed infraternal-dev-01-compute01.internal lookup returned fdc6:712e:ff18::1; ping received no replies, and SSH stopped at unknown-host-key verification before connecting, so I am not counting that direction as a new ping/SSH proof. The earlier two-node peer ping and compute01-to-dev-02 SSH evidence remains recorded in the Linux logs. Mac root resolver proof is pending the VM admin credential request.

Linux J254 cleanup finding: both owned node sessions 7313 (compute01) and 20780 (dev-02) accepted `{"command":"quit"}` and exited cleanly. Afterward, `ip link show dev infraternal0` reported “does not exist” on both hosts and `resolvectl status infraternal0` reported “No such device”; only the existing eth0/enp0s3/tailscale0/wt0 links and their vpn.kayg.org/reverse domains remain. The J254 claim is being released after this cleanup. Directional check on dev-02: its allowed `infraternal-dev-01-compute01.internal` lookup returned `fdc6:712e:ff18::1`; ping received no replies, and SSH stopped at unknown-host-key verification before connecting, so I am not counting that direction as a new ping/SSH proof. The earlier two-node peer ping and compute01-to-dev-02 SSH evidence remains recorded in the Linux logs. Mac root resolver proof is pending the VM admin credential request.
Author
Owner

Mac recheck finding at pushed head 2be58ce2: the first locked attempt exited before Cargo because its wrapper assumed /usr/bin/rustc; this VM keeps rustc/cargo under the user Cargo path. The lockf -k /tmp/mac-bench.lock wrapper ran its cleanup before releasing the lock: J254 target and temporary tree are absent, and 24 GiB are free. I am correcting PATH and repeating the package build/tests under the lock.

Mac recheck finding at pushed head 2be58ce2: the first locked attempt exited before Cargo because its wrapper assumed `/usr/bin/rustc`; this VM keeps rustc/cargo under the user Cargo path. The `lockf -k /tmp/mac-bench.lock` wrapper ran its cleanup before releasing the lock: J254 target and temporary tree are absent, and 24 GiB are free. I am correcting PATH and repeating the package build/tests under the lock.
Author
Owner

Second Mac recheck finding at 2be58ce2: the plain cargo test invocation compiled examples/kernel_node.rs, a Linux-only namespace fixture that uses the Linux wireguard and firewall modules, so it fails on macOS with agent,mesh-dns. This is outside the Mac test targets. I am rerunning the intended Mac feature test set with Cargo’s --tests selection so it compiles/runs the library and integration test targets without that Linux-only example; no production code changed.

Second Mac recheck finding at 2be58ce2: the plain `cargo test` invocation compiled `examples/kernel_node.rs`, a Linux-only namespace fixture that uses the Linux `wireguard` and `firewall` modules, so it fails on macOS with `agent,mesh-dns`. This is outside the Mac test targets. I am rerunning the intended Mac feature test set with Cargo’s `--tests` selection so it compiles/runs the library and integration test targets without that Linux-only example; no production code changed.
Author
Owner

Mac release-check finding at pushed head 2be58ce2: a direct Cargo release build reached the pinned shim build, then stopped because the non-Nix SSH shell lacks sha256sum. The lock wrapper removed the target and temporary tree before unlock (24 GiB free). Nix is installed at /nix/var/nix/profiles/default/bin/nix; I am using the documented nix develop -c scripts/macos-build-check path for the shipping binary and portable tests.

Mac release-check finding at pushed head 2be58ce2: a direct Cargo release build reached the pinned shim build, then stopped because the non-Nix SSH shell lacks `sha256sum`. The lock wrapper removed the target and temporary tree before unlock (24 GiB free). Nix is installed at `/nix/var/nix/profiles/default/bin/nix`; I am using the documented `nix develop -c scripts/macos-build-check` path for the shipping binary and portable tests.
Author
Owner

Mac test-matrix finding at pushed head 2be58ce2: the Nix-backed scripts/macos-build-check release artifact completed, and portable tests progressed, but Cargo stopped at tests/store.rs with E0433 (infraternal_plugin_mesh::dns is unavailable). The test target runs under default server+mesh-dns features, while dns is correctly compiled only with agent+mesh-dns. The lock wrapper removed the target and clone before unlock; 25 GiB are free. This matches the mesh feature-set regression called out in J208b/J208c; I am narrowing the test gate rather than changing production DNS visibility.

Mac test-matrix finding at pushed head 2be58ce2: the Nix-backed `scripts/macos-build-check` release artifact completed, and portable tests progressed, but Cargo stopped at `tests/store.rs` with E0433 (`infraternal_plugin_mesh::dns` is unavailable). The test target runs under default server+mesh-dns features, while `dns` is correctly compiled only with `agent+mesh-dns`. The lock wrapper removed the target and clone before unlock; 25 GiB are free. This matches the mesh feature-set regression called out in J208b/J208c; I am narrowing the test gate rather than changing production DNS visibility.
Author
Owner

J254 Mac feature-matrix finding at 2be58ce2: nix develop -c scripts/macos-build-check completed the release build, then failed compiling tests/store.rs with E0433. That integration test is enabled by mesh-dns, but it calls mesh::dns, which is correctly available only when agent and mesh-dns are both enabled (src/lib.rs:72). The prior Mac lock step removed its target and temporary checkout before unlock; the finding and full compiler output are in the immediately preceding Mac progress comments and /Users/lume/infraternal-ops/j254-evidence/mac-build-check-2be.log.

Plan: narrow the test cfg to agent + mesh-dns while retaining the store projection assertions; verify the default server+mesh-dns test target compiles and the agent/server/DNS variant runs on compute01; commit and push; run verify quick, verify fast, and the privileged row on compute01; rerun nix develop -c scripts/macos-build-check on the Mac under lockf -k /tmp/mac-bench.lock with CARGO_INCREMENTAL=0, deleting the target before unlocking. The Mac resolver root and userspace live proof is already recorded above; this rerun is for the shipping build and portable tests. Estimated active work: about 60 minutes, plus any build-slot wait.

J254 Mac feature-matrix finding at 2be58ce2: `nix develop -c scripts/macos-build-check` completed the release build, then failed compiling `tests/store.rs` with E0433. That integration test is enabled by `mesh-dns`, but it calls `mesh::dns`, which is correctly available only when `agent` and `mesh-dns` are both enabled (`src/lib.rs:72`). The prior Mac lock step removed its target and temporary checkout before unlock; the finding and full compiler output are in the immediately preceding Mac progress comments and `/Users/lume/infraternal-ops/j254-evidence/mac-build-check-2be.log`. Plan: narrow the test cfg to `agent + mesh-dns` while retaining the store projection assertions; verify the default server+mesh-dns test target compiles and the agent/server/DNS variant runs on compute01; commit and push; run `verify quick`, `verify fast`, and the privileged row on compute01; rerun `nix develop -c scripts/macos-build-check` on the Mac under `lockf -k /tmp/mac-bench.lock` with `CARGO_INCREMENTAL=0`, deleting the target before unlocking. The Mac resolver root and userspace live proof is already recorded above; this rerun is for the shipping build and portable tests. Estimated active work: about 60 minutes, plus any build-slot wait.
Author
Owner

The narrow test feature-gate fix is committed and pushed: 65f44579312b5a34d330135b79920440bb8cb556. A compute01 matrix check is waiting through build-slot; the Mac rerun is compiling this exact pushed snapshot under the required lock with incremental compilation off. No additional work is queued on dev-02.

The narrow test feature-gate fix is committed and pushed: `65f44579312b5a34d330135b79920440bb8cb556`. A compute01 matrix check is waiting through build-slot; the Mac rerun is compiling this exact pushed snapshot under the required lock with incremental compilation off. No additional work is queued on dev-02.
Author
Owner

Before final Linux gates I fetched current origin/dev: it has advanced 88 commits from this branch's merge base. The branch's 9 commits and those upstream commits currently touch disjoint paths. I am rebasing the J254 commits onto current origin/dev per the build brief, then I will run the gates against that base; the queued targeted matrix check remains on compute01 and the test tree is unchanged by the rebase.

Before final Linux gates I fetched current `origin/dev`: it has advanced 88 commits from this branch's merge base. The branch's 9 commits and those upstream commits currently touch disjoint paths. I am rebasing the J254 commits onto current `origin/dev` per the build brief, then I will run the gates against that base; the queued targeted matrix check remains on compute01 and the test tree is unchanged by the rebase.
Author
Owner

Rebase completed without conflicts and is pushed at 16a554d5a34168000ae81037a773e9a8422362c1 on current origin/dev 9630c2bd9. The previous Mac check passed on the same J254 changes before rebase; I am repeating the locked build-and-portable-tests check on this final tree. No resolver smoke paths or product source files changed during the rebase.

Rebase completed without conflicts and is pushed at `16a554d5a34168000ae81037a773e9a8422362c1` on current `origin/dev` `9630c2bd9`. The previous Mac check passed on the same J254 changes before rebase; I am repeating the locked build-and-portable-tests check on this final tree. No resolver smoke paths or product source files changed during the rebase.
Author
Owner

Current-dev integration check finding: after a temporary rebase onto origin/dev 9630c2bd9, the Mac release check failed in untouched upstream code at crates/infraternal-agent/src/logins.rs:424: SockFlag::SOCK_CLOEXEC is unavailable on macOS. The failing output is in /Users/lume/infraternal-ops/j254-evidence/mac-build-check-16a.log; the lock-held wrapper removed its target and source tree before unlock, restoring 23 GiB free. The task branch itself had already passed nix develop -c scripts/macos-build-check at 65f445793, with evidence in /Users/lume/infraternal-ops/j254-evidence/mac-build-check-65f.log. Since the failing file is upstream and outside J254, I restored the review branch to 65f445793 instead of adding an unrelated production change. The queued compute01 verify quick now targets that restored head; the stale target remains removed.

Current-dev integration check finding: after a temporary rebase onto `origin/dev` `9630c2bd9`, the Mac release check failed in untouched upstream code at `crates/infraternal-agent/src/logins.rs:424`: `SockFlag::SOCK_CLOEXEC` is unavailable on macOS. The failing output is in `/Users/lume/infraternal-ops/j254-evidence/mac-build-check-16a.log`; the lock-held wrapper removed its target and source tree before unlock, restoring 23 GiB free. The task branch itself had already passed `nix develop -c scripts/macos-build-check` at `65f445793`, with evidence in `/Users/lume/infraternal-ops/j254-evidence/mac-build-check-65f.log`. Since the failing file is upstream and outside J254, I restored the review branch to `65f445793` instead of adding an unrelated production change. The queued compute01 `verify quick` now targets that restored head; the stale target remains removed.
Author
Owner

Compute01 gate update at 65f44579312b5a34d330135b79920440bb8cb556: the fresh verify quick is queued at position 9 through build-slot on a clean J254 target. Four other builds are active; free disk is 96 GiB. The gate log is ~/j254-evidence/verify-quick-16a.log once it starts. I am waiting in the foreground; no work is running on dev-02.

Compute01 gate update at `65f44579312b5a34d330135b79920440bb8cb556`: the fresh `verify quick` is queued at position 9 through `build-slot` on a clean J254 target. Four other builds are active; free disk is 96 GiB. The gate log is `~/j254-evidence/verify-quick-16a.log` once it starts. I am waiting in the foreground; no work is running on dev-02.
Author
Owner

J254 Mac resolver follow-up is complete. Final branch mesh-dns-j254 is pushed and clean at 65f44579312b5a34d330135b79920440bb8cb556 (parent 2be58ce2c1777d9fea0d7190fbc8f3170420ff7d). The commit narrows the store DNS regression test to agent + mesh-dns, matching the module's feature gate while keeping the default server+mesh-dns Mac test set buildable.

On the Mac, nix develop -c scripts/macos-build-check passed at this head under lockf -k /tmp/mac-bench.lock with CARGO_INCREMENTAL=0. The mesh library passed 56/56 tests, tests/store.rs passed 10/10, the SSH library passed 113 with 2 ignored, and the shim session-shell tests passed 5/5. Verbatim final lines:

macos-build-check: PASS
macos-build-check: PASS at 65f445793

Evidence: /Users/lume/infraternal-ops/j254-evidence/mac-build-check-65f.log. The source tree and target were removed before unlock; /etc/resolver is absent and the Mac has 23 GiB free. The root resolver smoke for internal and mesh.j254.test, and the userspace smoke leaving /etc/resolver untouched, are documented above with /Users/lume/infraternal-ops/j254-evidence/mac-dns-resolver.log.

Current-dev integration was checked and found an unrelated macOS compile error in upstream crates/infraternal-agent/src/logins.rs:424 (SockFlag::SOCK_CLOEXEC); the failed run and cleanup are documented above. I restored this review branch to its original base instead of adding an unrelated fix.

Per the owner's latest direction, I did not rerun the full Linux gates after the Mac step: the branch had already passed verify fast at be5b4ea01, and the full fast output at 2be58ce2 remains recorded above. A fresh verify quick was queued on compute01 but had not started after 11 minutes; I canceled it before execution. No build is running or queued for J254, and the compute01 target is clean.

J254 Mac resolver follow-up is complete. Final branch `mesh-dns-j254` is pushed and clean at `65f44579312b5a34d330135b79920440bb8cb556` (parent `2be58ce2c1777d9fea0d7190fbc8f3170420ff7d`). The commit narrows the store DNS regression test to `agent + mesh-dns`, matching the module's feature gate while keeping the default server+mesh-dns Mac test set buildable. On the Mac, `nix develop -c scripts/macos-build-check` passed at this head under `lockf -k /tmp/mac-bench.lock` with `CARGO_INCREMENTAL=0`. The mesh library passed 56/56 tests, `tests/store.rs` passed 10/10, the SSH library passed 113 with 2 ignored, and the shim session-shell tests passed 5/5. Verbatim final lines: ``` macos-build-check: PASS macos-build-check: PASS at 65f445793 ``` Evidence: `/Users/lume/infraternal-ops/j254-evidence/mac-build-check-65f.log`. The source tree and target were removed before unlock; `/etc/resolver` is absent and the Mac has 23 GiB free. The root resolver smoke for `internal` and `mesh.j254.test`, and the userspace smoke leaving `/etc/resolver` untouched, are documented above with `/Users/lume/infraternal-ops/j254-evidence/mac-dns-resolver.log`. Current-dev integration was checked and found an unrelated macOS compile error in upstream `crates/infraternal-agent/src/logins.rs:424` (`SockFlag::SOCK_CLOEXEC`); the failed run and cleanup are documented above. I restored this review branch to its original base instead of adding an unrelated fix. Per the owner's latest direction, I did not rerun the full Linux gates after the Mac step: the branch had already passed `verify fast` at `be5b4ea01`, and the full fast output at `2be58ce2` remains recorded above. A fresh `verify quick` was queued on compute01 but had not started after 11 minutes; I canceled it before execution. No build is running or queued for J254, and the compute01 target is clean.
Author
Owner

J254 resume update after the Mac step and compute01 disk cleanup request:

  • Branch mesh-dns-j254 remains clean and pushed at 65f44579312b5a34d330135b79920440bb8cb556.
  • ~/worktrees/j254-current/target is already absent on compute01, and I found no J254 build process or queued command using that path.
  • Compute01 currently has 28 GiB free, all four build slots busy, and a long queue. Dev-02 has one free slot and 113 GiB free; I will use it only for native aarch64 checks with CARGO_INCREMENTAL=0, removing the job target afterward. The full verify fast and privileged row remain on compute01.
  • The original two-Linux-node acceptance still needs a complete repeat: allowed lookup plus ping/SSH, denied lookup byte-comparison with a nonexistent name, and clean resolver removal. I will claim both hosts before changing mesh state and will leave existing services untouched.

Next: run verify quick, finish the live Linux proof, then run the final compute01 verify fast (which includes the privileged integration row). No merge.

J254 resume update after the Mac step and compute01 disk cleanup request: - Branch `mesh-dns-j254` remains clean and pushed at `65f44579312b5a34d330135b79920440bb8cb556`. - `~/worktrees/j254-current/target` is already absent on compute01, and I found no J254 build process or queued command using that path. - Compute01 currently has 28 GiB free, all four build slots busy, and a long queue. Dev-02 has one free slot and 113 GiB free; I will use it only for native aarch64 checks with `CARGO_INCREMENTAL=0`, removing the job target afterward. The full `verify fast` and privileged row remain on compute01. - The original two-Linux-node acceptance still needs a complete repeat: allowed lookup plus ping/SSH, denied lookup byte-comparison with a nonexistent name, and clean resolver removal. I will claim both hosts before changing mesh state and will leave existing services untouched. Next: run `verify quick`, finish the live Linux proof, then run the final compute01 `verify fast` (which includes the privileged integration row). No merge.
Author
Owner

ARM quick-gate finding at 65f44579312b5a34d330135b79920440bb8cb556: verify quick passed its scope, migration, vendoring, formatting, documentation, clippy and diff-practice rows, then the changed-package unit tests reported 269 passed, 1 failed, 23 ignored. The failing test is daemon::remote_tests::silent_authenticated_peer_has_a_greeting_deadline at crates/infraternal-agent/src/daemon_remote_tests.rs:212; it timed out at the test's outer 8-second guard. The file is unchanged by J254. Its test wraps the dispatcher in 8 seconds while the dispatcher has a 5-second greeting deadline after startup, so I am reproducing that exact test alone before treating this as a timing flake or changing anything. The dev-02 target and scratch directory were removed after the run.

ARM quick-gate finding at `65f44579312b5a34d330135b79920440bb8cb556`: `verify quick` passed its scope, migration, vendoring, formatting, documentation, clippy and diff-practice rows, then the changed-package unit tests reported 269 passed, 1 failed, 23 ignored. The failing test is `daemon::remote_tests::silent_authenticated_peer_has_a_greeting_deadline` at `crates/infraternal-agent/src/daemon_remote_tests.rs:212`; it timed out at the test's outer 8-second guard. The file is unchanged by J254. Its test wraps the dispatcher in 8 seconds while the dispatcher has a 5-second greeting deadline after startup, so I am reproducing that exact test alone before treating this as a timing flake or changing anything. The dev-02 target and scratch directory were removed after the run.
Author
Owner

Follow-up to the ARM quick finding: I rebuilt the same agent library test set with default,native-streams,proxy features (293 tests total), selected only daemon::remote_tests::silent_authenticated_peer_has_a_greeting_deadline, and ran it with one test thread. It passed 1/1 in 5.01 s. The J254 diff does not touch the test file, and this agent test set does not enable the agent mesh feature, so DNS startup is not on this path. The full-run failure is isolated to concurrent test execution / scheduling around the test's 8-second outer guard and 5-second greeting timeout. I am rerunning verify quick with RUST_TEST_THREADS=1 to check the complete pre-gate without concurrent unit tests; no source change is planned unless that produces new evidence. The isolated target and /tmp state were removed.

Follow-up to the ARM quick finding: I rebuilt the same agent library test set with `default,native-streams,proxy` features (293 tests total), selected only `daemon::remote_tests::silent_authenticated_peer_has_a_greeting_deadline`, and ran it with one test thread. It passed 1/1 in 5.01 s. The J254 diff does not touch the test file, and this agent test set does not enable the agent `mesh` feature, so DNS startup is not on this path. The full-run failure is isolated to concurrent test execution / scheduling around the test's 8-second outer guard and 5-second greeting timeout. I am rerunning `verify quick` with `RUST_TEST_THREADS=1` to check the complete pre-gate without concurrent unit tests; no source change is planned unless that produces new evidence. The isolated target and `/tmp` state were removed.
Author
Owner

verify quick passed on the native aarch64 dev-02 host at 65f44579312b5a34d330135b79920440bb8cb556 with CARGO_INCREMENTAL=0 RUST_TEST_THREADS=1 (the serial setting avoids the existing 8-second greeting test timeout under parallel load). Counts: agent 270 passed/23 ignored; mesh 55 passed; server 551 passed/26 ignored; CLI 216 passed/15 ignored. Rust gate rows were all PASS, including workspace clippy and both changed-package test rows. Verbatim footer:

QUICK pre-check passed. This is not the gate: run scripts/verify fast before reporting done.
VERIFY_QUICK_EXIT=0

Log: /home/claude/infraternal-ops/j254-evidence/verify-quick-65f-aarch64-serial.log. The J254 target and scratch directory were removed; dev-02 has 112 GiB free.

`verify quick` passed on the native aarch64 dev-02 host at `65f44579312b5a34d330135b79920440bb8cb556` with `CARGO_INCREMENTAL=0 RUST_TEST_THREADS=1` (the serial setting avoids the existing 8-second greeting test timeout under parallel load). Counts: agent 270 passed/23 ignored; mesh 55 passed; server 551 passed/26 ignored; CLI 216 passed/15 ignored. Rust gate rows were all PASS, including workspace clippy and both changed-package test rows. Verbatim footer: ``` QUICK pre-check passed. This is not the gate: run scripts/verify fast before reporting done. VERIFY_QUICK_EXIT=0 ``` Log: `/home/claude/infraternal-ops/j254-evidence/verify-quick-65f-aarch64-serial.log`. The J254 target and scratch directory were removed; dev-02 has 112 GiB free.
Author
Owner

Linux proof resume at 65f4457931. The requested compute01 target is already absent; compute01 has 120 GiB free. Both systemd-resolved services are active and neither host has infraternal0, so no mesh state has changed. The live-state claim on compute01 is currently held by J287 (2026-11-06T15:52:51Z); dev-02 has no claim. I will wait in the foreground, then claim both before the two-node proof. Build-slot status: compute01 has all four slots busy with five queued jobs; dev-02 has one free slot and 112 GiB free. Mac resolver build and smoke evidence are already recorded above.

Linux proof resume at 65f44579312b5a34d330135b79920440bb8cb556. The requested compute01 target is already absent; compute01 has 120 GiB free. Both systemd-resolved services are active and neither host has infraternal0, so no mesh state has changed. The live-state claim on compute01 is currently held by J287 (2026-11-06T15:52:51Z); dev-02 has no claim. I will wait in the foreground, then claim both before the two-node proof. Build-slot status: compute01 has all four slots busy with five queued jobs; dev-02 has one free slot and 112 GiB free. Mac resolver build and smoke evidence are already recorded above.
Author
Owner

Live proof remains blocked by shared mesh serialization.

J287's claim cleared, but J279 acquired compute01 before my claim; noclobber rejected the write, so I have not claimed dev-02 or changed any mesh state. I am waiting in the foreground for J279 to finish. The unrelated services and interfaces remain untouched.

Live proof remains blocked by shared mesh serialization. J287's claim cleared, but J279 acquired compute01 before my claim; `noclobber` rejected the write, so I have not claimed dev-02 or changed any mesh state. I am waiting in the foreground for J279 to finish. The unrelated services and interfaces remain untouched.
Author
Owner

Compute01 x86 fixture build status: still at build-slot queue position 3 after 17 minutes. All four slots are occupied; free disk is 111 GiB, and the J254 target remains absent until the queued build starts. The aarch64 node/coordinator fixture build completed on dev-02 and its target is 1.3 GiB. I am waiting in the foreground; neither mesh host is claimed and no J254 mesh state has changed.

Compute01 x86 fixture build status: still at build-slot queue position 3 after 17 minutes. All four slots are occupied; free disk is 111 GiB, and the J254 target remains absent until the queued build starts. The aarch64 node/coordinator fixture build completed on dev-02 and its target is 1.3 GiB. I am waiting in the foreground; neither mesh host is claimed and no J254 mesh state has changed.
Author
Owner

Queue progress: one compute01 slot freed, moving the J254 x86 fixture from position 3 to position 2 (one queued job ahead). Free disk is 98 GiB. J279 still holds the live-mesh claim; I have not started either node.

Queue progress: one compute01 slot freed, moving the J254 x86 fixture from position 3 to position 2 (one queued job ahead). Free disk is 98 GiB. J279 still holds the live-mesh claim; I have not started either node.
Author
Owner

Compute01 queue blocker update after 30 minutes: J254's x86 node fixture remains position 2 with two jobs ahead. Active jobs have run about 1h17, 41m, 32m, and 32m; free disk is 93 GiB. J279 still holds the live-mesh claim. The aarch64 fixture is built and the meshtest SSH key was verified over the management link. No node or resolver state has been changed; I am continuing to wait in the foreground.

Compute01 queue blocker update after 30 minutes: J254's x86 node fixture remains position 2 with two jobs ahead. Active jobs have run about 1h17, 41m, 32m, and 32m; free disk is 93 GiB. J279 still holds the live-mesh claim. The aarch64 fixture is built and the meshtest SSH key was verified over the management link. No node or resolver state has been changed; I am continuing to wait in the foreground.
Author
Owner

Fixture builds are ready for the Linux proof. Compute01's x86 kernel_node was built through build-slot from clean head 65f4457931 (log: ~/infraternal-ops/j254-evidence/kernel-node-x86_64-build-65f.log). Dev-02 has the aarch64 kernel_node and temporary hostname-configured coordinator fixture built with CARGO_INCREMENTAL=0; the source was restored clean (logs: kernel-fixtures-aarch64-65f.log and coordinator-fixture-d339-aarch64-65f.log). Compute01 has 86 GiB free and a new 1.3 GiB target. J279's live-mesh claim remains, although neither host currently has infraternal0; I am waiting and have not started a node.

Fixture builds are ready for the Linux proof. Compute01's x86 kernel_node was built through build-slot from clean head 65f44579312b5a34d330135b79920440bb8cb556 (log: ~/infraternal-ops/j254-evidence/kernel-node-x86_64-build-65f.log). Dev-02 has the aarch64 kernel_node and temporary hostname-configured coordinator fixture built with CARGO_INCREMENTAL=0; the source was restored clean (logs: kernel-fixtures-aarch64-65f.log and coordinator-fixture-d339-aarch64-65f.log). Compute01 has 86 GiB free and a new 1.3 GiB target. J279's live-mesh claim remains, although neither host currently has infraternal0; I am waiting and have not started a node.
Author
Owner

Claim wait update: compute01 still has the unchanged J279 claim from 2026-11-06T16:15:33Z after repeated foreground checks; infraternal0 remains absent. Dev-02 remains unclaimed and also has no infraternal0. Both J254 fixtures are ready, but no node has started and resolver state is untouched.

Claim wait update: compute01 still has the unchanged J279 claim from 2026-11-06T16:15:33Z after repeated foreground checks; infraternal0 remains absent. Dev-02 remains unclaimed and also has no infraternal0. Both J254 fixtures are ready, but no node has started and resolver state is untouched.
Author
Owner

J254 review handoff: branch mesh-dns-j254, head 65f44579312b5a34d330135b79920440bb8cb556; the branch is clean and pushed.

  • macOS resolver build and smoke passed at this head under lockf -k /tmp/mac-bench.lock with CARGO_INCREMENTAL=0; the target was removed before releasing the lock, and /etc/resolver is absent afterward. Evidence: ~/infraternal-ops/j254-evidence/mac-build-check-65f.log and mac-dns-resolver.log.
  • verify quick passed at 65f4457 (exit 0): ~/infraternal-ops/j254-evidence/verify-quick-65f-aarch64-serial.log.
  • verify fast passed at 2be58ce (exit 0); the privileged integration row passed 96/96. The earlier fast run at be5b4ea also passed. Evidence: ~/infraternal-ops/j254-evidence/verify-fast-final.log. Per the owner's latest direction, I did not rerun the full gate after the Mac step.
  • Recorded DNS measurements: p50 75 µs, p99 90 µs, 105,328.7 queries/s, and 135,168 bytes per 1,000 names. Isolated Hickory parser/encoder size increase: 344,864 bytes.
  • Linux two-VM live proof remains pending. J279 still held the live-mesh claim (2026-11-06T16:15:33Z); neither VM had infraternal0, and I did not start nodes or change resolver state. I left the claim untouched.
  • I removed compute01's ~/worktrees/j254-current/target after checking build-slot --status and confirming no J254 build was active or queued against it. It was 1.3 GiB at inspection; compute01 showed 65 GiB free after removal.

Handing this head to review as directed; the Linux live-proof gap is explicit above.

J254 review handoff: branch `mesh-dns-j254`, head `65f44579312b5a34d330135b79920440bb8cb556`; the branch is clean and pushed. - macOS resolver build and smoke passed at this head under `lockf -k /tmp/mac-bench.lock` with `CARGO_INCREMENTAL=0`; the target was removed before releasing the lock, and `/etc/resolver` is absent afterward. Evidence: `~/infraternal-ops/j254-evidence/mac-build-check-65f.log` and `mac-dns-resolver.log`. - `verify quick` passed at `65f4457` (exit 0): `~/infraternal-ops/j254-evidence/verify-quick-65f-aarch64-serial.log`. - `verify fast` passed at `2be58ce` (exit 0); the privileged integration row passed 96/96. The earlier fast run at `be5b4ea` also passed. Evidence: `~/infraternal-ops/j254-evidence/verify-fast-final.log`. Per the owner's latest direction, I did not rerun the full gate after the Mac step. - Recorded DNS measurements: p50 75 µs, p99 90 µs, 105,328.7 queries/s, and 135,168 bytes per 1,000 names. Isolated Hickory parser/encoder size increase: 344,864 bytes. - Linux two-VM live proof remains pending. J279 still held the live-mesh claim (`2026-11-06T16:15:33Z`); neither VM had `infraternal0`, and I did not start nodes or change resolver state. I left the claim untouched. - I removed compute01's `~/worktrees/j254-current/target` after checking `build-slot --status` and confirming no J254 build was active or queued against it. It was 1.3 GiB at inspection; compute01 showed 65 GiB free after removal. Handing this head to review as directed; the Linux live-proof gap is explicit above.
Author
Owner

J254 continuation status: compute01 still shows the unchanged J279 mesh claim (2026-11-06T16:15:33Z) and neither VM has infraternal0. I have not started a node or changed resolver state. The clean compute01 checkout is at 65f44579312b5a34d330135b79920440bb8cb556, with its target absent; the needed kernel_node example rebuild is waiting in the foreground at build-slot queue position 14. Dev-02 currently has a detached checkout with local mesh source modifications and an untracked target, so I left it untouched. I will use the existing no-clobber claim procedure before any live proof.

J254 continuation status: compute01 still shows the unchanged J279 mesh claim (`2026-11-06T16:15:33Z`) and neither VM has `infraternal0`. I have not started a node or changed resolver state. The clean compute01 checkout is at `65f44579312b5a34d330135b79920440bb8cb556`, with its target absent; the needed `kernel_node` example rebuild is waiting in the foreground at build-slot queue position 14. Dev-02 currently has a detached checkout with local mesh source modifications and an untracked target, so I left it untouched. I will use the existing no-clobber claim procedure before any live proof.
Author
Owner

J254 continuation update at head 65f44579312b5a34d330135b79920440bb8cb556:

  • The macOS resolver slice is complete and the branch is clean and pushed. The locked Mac build/smoke passed with CARGO_INCREMENTAL=0; evidence is in mac-build-check-65f.log and mac-dns-resolver.log under ~/infraternal-ops/j254-evidence/ on the Mac. The target was removed before unlocking.
  • Compute01's ~/worktrees/j254-current/target was 1.3 GiB when checked. No J254 build was active or queued against it, so I removed it; the target is absent and compute01 showed 190 GiB free on the latest check.
  • The mesh claim file is currently absent on compute01; dev-02 has no claim or infraternal0. I made no live mesh changes.
  • Existing validation evidence remains: verify quick passed at 65f4457; verify fast passed at 2be58ce, including the privileged integration row (96/96). Logs are verify-quick-65f-aarch64-serial.log and verify-fast-final.log under ~/infraternal-ops/j254-evidence/. Per the owner's instruction, I did not rerun the full Linux gate after the Mac step.

The full two-Linux-node acceptance is still pending: allowed-name lookup plus ping/SSH, denied-name response equality with a nonexistent name, and resolver removal have not been proven end to end. I am handing 65f44579312b5a34d330135b79920440bb8cb556 to review as directed, with that gap explicit.

J254 continuation update at head `65f44579312b5a34d330135b79920440bb8cb556`: - The macOS resolver slice is complete and the branch is clean and pushed. The locked Mac build/smoke passed with `CARGO_INCREMENTAL=0`; evidence is in `mac-build-check-65f.log` and `mac-dns-resolver.log` under `~/infraternal-ops/j254-evidence/` on the Mac. The target was removed before unlocking. - Compute01's `~/worktrees/j254-current/target` was 1.3 GiB when checked. No J254 build was active or queued against it, so I removed it; the target is absent and compute01 showed 190 GiB free on the latest check. - The mesh claim file is currently absent on compute01; dev-02 has no claim or `infraternal0`. I made no live mesh changes. - Existing validation evidence remains: `verify quick` passed at `65f4457`; `verify fast` passed at `2be58ce`, including the privileged integration row (96/96). Logs are `verify-quick-65f-aarch64-serial.log` and `verify-fast-final.log` under `~/infraternal-ops/j254-evidence/`. Per the owner's instruction, I did not rerun the full Linux gate after the Mac step. The full two-Linux-node acceptance is still pending: allowed-name lookup plus ping/SSH, denied-name response equality with a nonexistent name, and resolver removal have not been proven end to end. I am handing `65f44579312b5a34d330135b79920440bb8cb556` to review as directed, with that gap explicit.
Author
Owner

J254 Linux proof resumed at pushed head 65f44579312b5a34d330135b79920440bb8cb556.

  • Both VMs have systemd-resolved active and no infraternal0. I captured pre-run resolver snapshots in resolver-baseline-compute01-65f.txt and resolver-baseline-dev-02-65f.txt under ~/infraternal-ops/j254-evidence/.
  • Dev-02’s existing j254-current checkout is still dirty, so I left it untouched and created a clean proof worktree at the same pushed head. The ARM node/coordinator fixtures built there with CARGO_INCREMENTAL=0; logs are linux-fixtures-aarch64-65f.log and linux-coordinator-fixture-65f.log.
  • The coordinator fixture is temporarily configured in that isolated worktree to use the VMs’ reported D339 hostnames, their wt0 outer addresses, and custom suffix mesh.j254.test alongside .internal. It is test-only and will be restored before the gate.
  • The x86_64 Linux node fixture built as a static musl release artifact on dev-02 and was copied to compute01. SHA-256: 82a6a0c0235e60ce03eb6474c57e1c014246a4effc93c1d4454459f621a3665f. Build log: linux-fixture-x86_64-release-cross-65f.log.

J325 currently holds ~/mesh-vm.claim; its test scenario and server processes are live. I have not claimed either VM or changed resolver/interface state. I will claim using the documented no-clobber procedure only after J325 releases it, then run the two-node lookup, ping/SSH, denied-versus-nonexistent response, and cleanup checks.

J254 Linux proof resumed at pushed head `65f44579312b5a34d330135b79920440bb8cb556`. - Both VMs have `systemd-resolved` active and no `infraternal0`. I captured pre-run resolver snapshots in `resolver-baseline-compute01-65f.txt` and `resolver-baseline-dev-02-65f.txt` under `~/infraternal-ops/j254-evidence/`. - Dev-02’s existing `j254-current` checkout is still dirty, so I left it untouched and created a clean proof worktree at the same pushed head. The ARM node/coordinator fixtures built there with `CARGO_INCREMENTAL=0`; logs are `linux-fixtures-aarch64-65f.log` and `linux-coordinator-fixture-65f.log`. - The coordinator fixture is temporarily configured in that isolated worktree to use the VMs’ reported D339 hostnames, their `wt0` outer addresses, and custom suffix `mesh.j254.test` alongside `.internal`. It is test-only and will be restored before the gate. - The x86_64 Linux node fixture built as a static musl release artifact on dev-02 and was copied to compute01. SHA-256: `82a6a0c0235e60ce03eb6474c57e1c014246a4effc93c1d4454459f621a3665f`. Build log: `linux-fixture-x86_64-release-cross-65f.log`. J325 currently holds `~/mesh-vm.claim`; its test scenario and server processes are live. I have not claimed either VM or changed resolver/interface state. I will claim using the documented no-clobber procedure only after J325 releases it, then run the two-node lookup, ping/SSH, denied-versus-nonexistent response, and cleanup checks.
Author
Owner

J254 progress at pushed head 65f44579312b5a34d330135b79920440bb8cb556 (mesh-dns-j254):

  • The current-head macOS mesh unit run passed under lockf -k /tmp/mac-bench.lock with CARGO_INCREMENTAL=0: test result: ok. 112 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 7.66s. Log: /Users/lume/infraternal-ops/j254-evidence/macos-unit-tests-65f4457.log. The target and my temporary Mac worktree were removed; 23 GiB are free.
  • The live Mac resolver root and userspace smoke tests remain green in /Users/lume/infraternal-ops/j254-evidence/mac-dns-resolver.log; the reload probe is /Users/lume/infraternal-ops/j254-evidence/mac-resolver-reload.log. The root test installed and removed its owned suffix route; the userspace test left system resolver state unchanged.
  • dev-02 had no active build-slot or Rust process when checked. I removed its stale ~/worktrees/j254-linux-proof-20261106/target (44 GiB); free space is now 110 GiB. No additional aarch64 build was started, following the one-batched-run limit in BRIEF-COMMON.md.
  • compute01's ~/worktrees/j254-current/target is absent, no J254 build is queued, and free space is 181 GiB. The remaining two-Linux-node live proof is pending while J325 owns the shared mesh claim (J325 2026-11-06T18:50:01Z). I have not changed mesh state. I will continue that proof on compute01 when the claim clears, using the existing fixture artifacts.
J254 progress at pushed head `65f44579312b5a34d330135b79920440bb8cb556` (`mesh-dns-j254`): - The current-head macOS mesh unit run passed under `lockf -k /tmp/mac-bench.lock` with `CARGO_INCREMENTAL=0`: `test result: ok. 112 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 7.66s`. Log: `/Users/lume/infraternal-ops/j254-evidence/macos-unit-tests-65f4457.log`. The target and my temporary Mac worktree were removed; 23 GiB are free. - The live Mac resolver root and userspace smoke tests remain green in `/Users/lume/infraternal-ops/j254-evidence/mac-dns-resolver.log`; the reload probe is `/Users/lume/infraternal-ops/j254-evidence/mac-resolver-reload.log`. The root test installed and removed its owned suffix route; the userspace test left system resolver state unchanged. - dev-02 had no active build-slot or Rust process when checked. I removed its stale `~/worktrees/j254-linux-proof-20261106/target` (44 GiB); free space is now 110 GiB. No additional aarch64 build was started, following the one-batched-run limit in `BRIEF-COMMON.md`. - compute01's `~/worktrees/j254-current/target` is absent, no J254 build is queued, and free space is 181 GiB. The remaining two-Linux-node live proof is pending while J325 owns the shared mesh claim (`J325 2026-11-06T18:50:01Z`). I have not changed mesh state. I will continue that proof on compute01 when the claim clears, using the existing fixture artifacts.
Author
Owner

Linux live-proof wait update at 65f44579312b5a34d330135b79920440bb8cb556: the J325 claim cleared, but J315 acquired ~/mesh-vm.claim (2026-11-06T19:37:03Z) before J254 could claim it. infraternal0 remains absent on compute01. I have not started nodes or changed resolver state.

The dev-02 J254 target remains removed (110 GiB free); no further aarch64 build is planned. The existing x86 node and coordinator fixture artifacts are ready. I will run the remaining live proof from compute01 after the mesh claim is available.

Linux live-proof wait update at `65f44579312b5a34d330135b79920440bb8cb556`: the J325 claim cleared, but J315 acquired `~/mesh-vm.claim` (`2026-11-06T19:37:03Z`) before J254 could claim it. `infraternal0` remains absent on compute01. I have not started nodes or changed resolver state. The dev-02 J254 target remains removed (110 GiB free); no further aarch64 build is planned. The existing x86 node and coordinator fixture artifacts are ready. I will run the remaining live proof from compute01 after the mesh claim is available.
Author
Owner

The J315 claim cleared, but J328 acquired ~/mesh-vm.claim (2026-11-06T19:54:08Z) before J254 could claim the live mesh. infraternal0 is still absent; no J254 node or resolver state has changed. The dev-02 target remains removed, and the prepared fixtures are unchanged. I am waiting for the shared claim to clear before running the remaining proof from compute01.

The J315 claim cleared, but J328 acquired `~/mesh-vm.claim` (`2026-11-06T19:54:08Z`) before J254 could claim the live mesh. `infraternal0` is still absent; no J254 node or resolver state has changed. The dev-02 target remains removed, and the prepared fixtures are unchanged. I am waiting for the shared claim to clear before running the remaining proof from compute01.
Author
Owner

Claim wait update: J328 still owns ~/mesh-vm.claim (2026-11-06T19:54:08Z) after repeated foreground checks. The J254 Linux fixtures are ready and neither live mesh node has been changed. The dev-02 44 GiB target is removed; I am waiting for the claim before running the proof from compute01.

Claim wait update: J328 still owns `~/mesh-vm.claim` (`2026-11-06T19:54:08Z`) after repeated foreground checks. The J254 Linux fixtures are ready and neither live mesh node has been changed. The dev-02 44 GiB target is removed; I am waiting for the claim before running the proof from compute01.
Author
Owner

Linux proof preparation at 65f44579312b5a34d330135b79920440bb8cb556: the dev-02 44 GiB target is removed; that host now has 110 GiB free. The preserved ARM coordinator fixture is in ~/infraternal-ops/j254-evidence/coordinator-fixture-d339-65f; the ARM kernel_node artifact was only in the deleted target, while the x86_64 node binary remains on compute01.

Following BRIEF-COMMON.md's one-batched-aarch64-run limit, I will not start another dev-02 build. The pinned toolchain on compute01 has aarch64-unknown-linux-musl, and scripts/static-cargo supports that target, so I released my mesh claim without changing live state and will build the missing ARM node fixture on compute01 through build-slot, then copy the binary to dev-02 for the two-VM proof. At the last check compute01 had 4/4 slots busy and a long queue; I will wait in the foreground.

Linux proof preparation at `65f44579312b5a34d330135b79920440bb8cb556`: the dev-02 44 GiB target is removed; that host now has 110 GiB free. The preserved ARM coordinator fixture is in `~/infraternal-ops/j254-evidence/coordinator-fixture-d339-65f`; the ARM `kernel_node` artifact was only in the deleted target, while the x86_64 node binary remains on compute01. Following `BRIEF-COMMON.md`'s one-batched-aarch64-run limit, I will not start another dev-02 build. The pinned toolchain on compute01 has `aarch64-unknown-linux-musl`, and `scripts/static-cargo` supports that target, so I released my mesh claim without changing live state and will build the missing ARM node fixture on compute01 through `build-slot`, then copy the binary to dev-02 for the two-VM proof. At the last check compute01 had 4/4 slots busy and a long queue; I will wait in the foreground.
Author
Owner

Compute01 maintenance wait at 65f44579312b5a34d330135b79920440bb8cb556: the aarch64 musl kernel_node cross-build is still at build-slot queue position 24 and has not started; ~/worktrees/j254-current/target does not exist. Build-slot admission is frozen for maintenance, so I am leaving this foreground waiter for the planned restart cut and will treat that interruption as infrastructure, not a test failure. The build script and log path are ~/infraternal-ops/j254-evidence/build-kernel-node-aarch64-musl-65f.sh and kernel-node-aarch64-musl-build-65f.log. I will resubmit once in the foreground after the orchestrator posts compute01 back, 5 slots.

Compute01 maintenance wait at `65f44579312b5a34d330135b79920440bb8cb556`: the aarch64 musl `kernel_node` cross-build is still at build-slot queue position 24 and has not started; `~/worktrees/j254-current/target` does not exist. Build-slot admission is frozen for maintenance, so I am leaving this foreground waiter for the planned restart cut and will treat that interruption as infrastructure, not a test failure. The build script and log path are `~/infraternal-ops/j254-evidence/build-kernel-node-aarch64-musl-65f.sh` and `kernel-node-aarch64-musl-build-65f.log`. I will resubmit once in the foreground after the orchestrator posts `compute01 back, 5 slots`.
Author
Owner

J254 restart note: the queued aarch64 kernel_node cross-build at position 24 was cut when compute01 restarted. SSH returned exit 255 (Connection to infraternal-dev-01-compute01 closed by remote host) before compiler work started; this was an infrastructure interruption, not a build failure, and no target was created. The owner has posted compute01 back with 5 slots. I am resubmitting the prepared cross-build once through ~/infraternal-ops/build-slot in the foreground, then continuing the Linux two-node resolver proof.

J254 restart note: the queued aarch64 `kernel_node` cross-build at position 24 was cut when compute01 restarted. SSH returned exit 255 (`Connection to infraternal-dev-01-compute01 closed by remote host`) before compiler work started; this was an infrastructure interruption, not a build failure, and no target was created. The owner has posted compute01 back with 5 slots. I am resubmitting the prepared cross-build once through `~/infraternal-ops/build-slot` in the foreground, then continuing the Linux two-node resolver proof.
Author
Owner

J254 progress: the one foreground build-slot submission is live (compute01 PID 132001), waiting FIFO at position 14. All five slots are currently occupied; 138 GB is free, and the target has not been created. I am continuing to wait in the same session.

J254 progress: the one foreground `build-slot` submission is live (compute01 PID 132001), waiting FIFO at position 14. All five slots are currently occupied; 138 GB is free, and the target has not been created. I am continuing to wait in the same session.
Author
Owner

J254 build finding: after the foreground waiter acquired slot 2 at 22:53:39, the script exited 127 before compilation with env: cargo: No such file or directory. Its cleanup trap removed ~/worktrees/j254-current/target (verified absent). scripts/auth-toolchain only provisions Zig; scripts/static-cargo execs cargo, and this run was outside nix develop. I am checking the documented Nix shell provides Cargo before one corrected foreground submission.

J254 build finding: after the foreground waiter acquired slot 2 at 22:53:39, the script exited 127 before compilation with `env: cargo: No such file or directory`. Its cleanup trap removed `~/worktrees/j254-current/target` (verified absent). `scripts/auth-toolchain` only provisions Zig; `scripts/static-cargo` execs `cargo`, and this run was outside `nix develop`. I am checking the documented Nix shell provides Cargo before one corrected foreground submission.
Author
Owner

J254 toolchain diagnosis: nix develop -c cargo --version succeeded on compute01 with cargo 1.99.0 (5f94df478 2026-08-27), confirming the missing PATH entry came from running outside the project dev shell. I am submitting the corrected cross-build once through build-slot inside nix develop; the earlier no-compile failure log is preserved.

J254 toolchain diagnosis: `nix develop -c cargo --version` succeeded on compute01 with `cargo 1.99.0 (5f94df478 2026-08-27)`, confirming the missing PATH entry came from running outside the project dev shell. I am submitting the corrected cross-build once through build-slot inside `nix develop`; the earlier no-compile failure log is preserved.
Author
Owner

J254 ARM fixture build passed on compute01 at branch head 65f44579312b5a34d330135b79920440bb8cb556: scripts/static-cargo build --locked --release --target aarch64-unknown-linux-musl -p infraternal-plugin-mesh --features agent --example kernel_node, run through build-slot nix develop -c. Cargo finished release in 4m50s. Output is a statically linked, stripped AArch64 ELF, 5,622,864 bytes, SHA-256 7fdfc1fc15b18c92a6523f90f253006493a0a92ea9e409dfa760154c93317110 at ~/infraternal-ops/j254-evidence/kernel-node-aarch64-musl-65f. The target cleanup verified clean. I am copying the fixture to dev-02; no build is planned there.

J254 ARM fixture build passed on compute01 at branch head `65f44579312b5a34d330135b79920440bb8cb556`: `scripts/static-cargo build --locked --release --target aarch64-unknown-linux-musl -p infraternal-plugin-mesh --features agent --example kernel_node`, run through `build-slot nix develop -c`. Cargo finished release in 4m50s. Output is a statically linked, stripped AArch64 ELF, 5,622,864 bytes, SHA-256 `7fdfc1fc15b18c92a6523f90f253006493a0a92ea9e409dfa760154c93317110` at `~/infraternal-ops/j254-evidence/kernel-node-aarch64-musl-65f`. The target cleanup verified clean. I am copying the fixture to dev-02; no build is planned there.
Author
Owner

The ARM kernel_node artifact is now on dev-02; its SHA-256 matches compute01 exactly (7fdfc1fc15b18c92a6523f90f253006493a0a92ea9e409dfa760154c93317110), size 5,622,864 bytes, mode 755. The incoming file was atomically moved into place after the checksum check. No dev-02 build ran. I am proceeding to claim the shared live-mesh state before the two-node proof.

The ARM `kernel_node` artifact is now on dev-02; its SHA-256 matches compute01 exactly (`7fdfc1fc15b18c92a6523f90f253006493a0a92ea9e409dfa760154c93317110`), size 5,622,864 bytes, mode 755. The incoming file was atomically moved into place after the checksum check. No dev-02 build ran. I am proceeding to claim the shared live-mesh state before the two-node proof.
Author
Owner

J254 live-mesh proof is waiting: the no-clobber claim on compute01 reported J197 2026-11-06T22:41:10Z. The write failed without overwriting it, and I made no live mesh changes. I am waiting in the foreground for the shared claim to clear before retrying.

J254 live-mesh proof is waiting: the no-clobber claim on compute01 reported `J197 2026-11-06T22:41:10Z`. The write failed without overwriting it, and I made no live mesh changes. I am waiting in the foreground for the shared claim to clear before retrying.
Author
Owner

J254 current-head gate status: verify fast at 65f44579312b5a34d330135b79920440bb8cb556 is the single foreground build-slot waiter (compute01 PID 2248322), FIFO position 23. All five slots are busy and free disk is exactly the 25 GB floor; no target or gate output has started. This is a scheduling wait, not a test result.

J254 current-head gate status: `verify fast` at `65f44579312b5a34d330135b79920440bb8cb556` is the single foreground build-slot waiter (compute01 PID 2248322), FIFO position 23. All five slots are busy and free disk is exactly the 25 GB floor; no target or gate output has started. This is a scheduling wait, not a test result.
Author
Owner

Queue update: compute01 free disk has fallen to 15 GB, below build-slot’s 25 GB floor. The current-head verify fast waiter remains at PID 2248322 / position 23; it has not created a target or started tests. I am waiting for disk and a slot to become available.

Queue update: compute01 free disk has fallen to 15 GB, below build-slot’s 25 GB floor. The current-head `verify fast` waiter remains at PID 2248322 / position 23; it has not created a target or started tests. I am waiting for disk and a slot to become available.
Author
Owner

Admission update: compute01 reports only 2 GB free, below the 25 GB floor. Slot 5 is free, but the current-head verify fast ticket remains queued and has not created target. J254’s target was cleaned after the ARM fixture build. I will not remove another job’s files; waiting for disk recovery.

Admission update: compute01 reports only 2 GB free, below the 25 GB floor. Slot 5 is free, but the current-head `verify fast` ticket remains queued and has not created `target`. J254’s target was cleaned after the ARM fixture build. I will not remove another job’s files; waiting for disk recovery.
Author
Owner

J254 fast gate progress: the admitted current-head run is compiling; target is 6.9 GB and compute01 now has 19 GB free, below the 25 GB admission floor (it started with 54 GB free). No failure has occurred. I am monitoring the foreground run and will not touch other jobs’ files.

J254 fast gate progress: the admitted current-head run is compiling; target is 6.9 GB and compute01 now has 19 GB free, below the 25 GB admission floor (it started with 54 GB free). No failure has occurred. I am monitoring the foreground run and will not touch other jobs’ files.
Author
Owner

Disk pressure during J254 current-head fast gate: the run is still compiling; its target is 42 GB and compute01 has 9.4 GB free (below the build-slot 25 GB floor). It was admitted at 54 GB free. I am monitoring the foreground run and have not touched other jobs’ files.

Disk pressure during J254 current-head fast gate: the run is still compiling; its target is 42 GB and compute01 has 9.4 GB free (below the build-slot 25 GB floor). It was admitted at 54 GB free. I am monitoring the foreground run and have not touched other jobs’ files.
Author
Owner

J254 compute01 maintenance note at head 65f44579312b5a34d330135b79920440bb8cb556:

The current-head scripts/verify fast run was cut by the planned compute01 restart. Its last emitted table showed test FAIL (101). Per the maintenance notice, I am treating that as an infrastructure interruption, not as a test failure or a passing gate.

I have not queued or started new compute01 work while admission is frozen. After the explicit compute01 back, 5 slots notice, I will resubmit scripts/verify fast once in the foreground and report its verbatim result.

J254 compute01 maintenance note at head `65f44579312b5a34d330135b79920440bb8cb556`: The current-head `scripts/verify fast` run was cut by the planned compute01 restart. Its last emitted table showed `test FAIL (101)`. Per the maintenance notice, I am treating that as an infrastructure interruption, not as a test failure or a passing gate. I have not queued or started new compute01 work while admission is frozen. After the explicit `compute01 back, 5 slots` notice, I will resubmit `scripts/verify fast` once in the foreground and report its verbatim result.
Author
Owner

Correction to my maintenance note on #266:

The pre-restart waiter was cut by the compute01 restart. The later current-head scripts/verify fast run at 65f44579312b5a34d330135b79920440bb8cb556 started after the #402 “compute01 back” notice and completed; it was not cut by that restart.

Its test row failed on infraternal-plugin-proxy: supervisor_tests::mesh_grant_revocation_removes_the_route_before_narrowing_its_allowlist panicked at crates/plugins/proxy/src/supervisor_tests.rs:513 with NotADirectory while starting the authorized route. The test target reported 122 passed; 1 failed. The privileged integration and static-build rows passed. The failing test file has no diff against fetched origin/dev at fba745051, but I have not yet proved the failure on that baseline.

Current capacity: compute01 has 19 GiB free, below the 25 GiB build-slot floor, with 11 queued jobs. dev-02 has 58 GiB free but its single slot is occupied by another full test and clippy run. I have queued no further J254 build or test; next I will inspect the fixture paths and reproduce on the unchanged baseline when a short slot is available.

Correction to my maintenance note on #266: The pre-restart waiter was cut by the compute01 restart. The later current-head `scripts/verify fast` run at `65f44579312b5a34d330135b79920440bb8cb556` started after the #402 “compute01 back” notice and completed; it was not cut by that restart. Its test row failed on `infraternal-plugin-proxy`: `supervisor_tests::mesh_grant_revocation_removes_the_route_before_narrowing_its_allowlist` panicked at `crates/plugins/proxy/src/supervisor_tests.rs:513` with `NotADirectory` while starting the authorized route. The test target reported `122 passed; 1 failed`. The privileged integration and static-build rows passed. The failing test file has no diff against fetched `origin/dev` at `fba745051`, but I have not yet proved the failure on that baseline. Current capacity: compute01 has 19 GiB free, below the 25 GiB build-slot floor, with 11 queued jobs. dev-02 has 58 GiB free but its single slot is occupied by another full test and clippy run. I have queued no further J254 build or test; next I will inspect the fixture paths and reproduce on the unchanged baseline when a short slot is available.
Author
Owner

J254 continuation status at branch head 65f44579312b5a34d330135b79920440bb8cb556:

  • The post-restart scripts/verify fast run completed; it was not cut by maintenance. Its test row failed in the unchanged proxy test supervisor_tests::mesh_grant_revocation_removes_the_route_before_narrowing_its_allowlist: NotADirectory at crates/plugins/proxy/src/supervisor_tests.rs:513, with 122 passed and 1 failed (verify-fast-65f.log, lines 3750–3774). The file has no diff against origin/dev, but this is not yet baseline-proven.
  • The branch is 9 commits ahead and 107 behind current origin/dev (65f4457 vs fba7450); PROCESS requires rebasing before the final gate. I will rebase the nine J254 commits and recheck the failure on the current base before deciding whether it is a baseline issue.
  • As instructed, I checked compute01's build-slot and process list; no J254 build was active or queued against ~/worktrees/j254-current/target. I deleted that 75 GB target. It is absent, and compute01 free space rose from 14 GB to 99 GB immediately after cleanup (126 GB at the later status check).
  • I have not queued a new build or touched live mesh state. The compute01 mesh claim is held by J197 (2026-11-06T22:41:10Z); dev-02 has no claim and its J254 target is absent. dev-02 currently has one busy slot and one queued job, so I am keeping remaining Linux proof work on compute01 as directed.
J254 continuation status at branch head `65f44579312b5a34d330135b79920440bb8cb556`: - The post-restart `scripts/verify fast` run completed; it was not cut by maintenance. Its test row failed in the unchanged proxy test `supervisor_tests::mesh_grant_revocation_removes_the_route_before_narrowing_its_allowlist`: `NotADirectory` at `crates/plugins/proxy/src/supervisor_tests.rs:513`, with 122 passed and 1 failed (`verify-fast-65f.log`, lines 3750–3774). The file has no diff against `origin/dev`, but this is not yet baseline-proven. - The branch is 9 commits ahead and 107 behind current `origin/dev` (`65f4457` vs `fba7450`); PROCESS requires rebasing before the final gate. I will rebase the nine J254 commits and recheck the failure on the current base before deciding whether it is a baseline issue. - As instructed, I checked compute01's build-slot and process list; no J254 build was active or queued against `~/worktrees/j254-current/target`. I deleted that 75 GB target. It is absent, and compute01 free space rose from 14 GB to 99 GB immediately after cleanup (126 GB at the later status check). - I have not queued a new build or touched live mesh state. The compute01 mesh claim is held by J197 (`2026-11-06T22:41:10Z`); dev-02 has no claim and its J254 target is absent. dev-02 currently has one busy slot and one queued job, so I am keeping remaining Linux proof work on compute01 as directed.
Author
Owner

J254 rebase update: I fetched current origin/dev at fba745051416c60a8523f0fe529c2aee6bbe0dd0, replayed all 9 J254 commits without conflicts, and force-with-lease pushed the updated branch at f3af3168118e188dee6d0824ab8cc05cddbfdba9. The prior post-restart fast run was on the old 65f4457 base and failed only at the proxy test documented above. Following PROCESS, I will run verify quick on the rebased head first, then reproduce the proxy test on current origin/dev to classify the failure; no new build has started yet.

J254 rebase update: I fetched current `origin/dev` at `fba745051416c60a8523f0fe529c2aee6bbe0dd0`, replayed all 9 J254 commits without conflicts, and force-with-lease pushed the updated branch at `f3af3168118e188dee6d0824ab8cc05cddbfdba9`. The prior post-restart fast run was on the old `65f4457` base and failed only at the proxy test documented above. Following PROCESS, I will run `verify quick` on the rebased head first, then reproduce the proxy test on current `origin/dev` to classify the failure; no new build has started yet.
Author
Owner

J254 current-base gate: the rebased head f3af3168118e188dee6d0824ab8cc05cddbfdba9 is synced on compute01. nix develop /home/claude/worktrees/j254-current -c cargo --version returned Cargo 1.99.0. The compute01 FIFO currently has all 5 slots occupied; dev-02's single slot is also busy. I am submitting scripts/verify quick once through compute01's build-slot, using the checkout's target/ for both quick and final fast. Log: ~/infraternal-ops/j254-evidence/verify-quick-f3.log.

J254 current-base gate: the rebased head `f3af3168118e188dee6d0824ab8cc05cddbfdba9` is synced on compute01. `nix develop /home/claude/worktrees/j254-current -c cargo --version` returned Cargo 1.99.0. The compute01 FIFO currently has all 5 slots occupied; dev-02's single slot is also busy. I am submitting `scripts/verify quick` once through compute01's `build-slot`, using the checkout's `target/` for both quick and final fast. Log: `~/infraternal-ops/j254-evidence/verify-quick-f3.log`.
Author
Owner

Queue update on rebased head f3af3168118e188dee6d0824ab8cc05cddbfdba9: the single verify quick foreground ticket is still waiting at compute01 build-slot position 5 after about 10 minutes. It has not started Cargo or created target/; compute01 has 99 GiB free. dev-02 remains occupied, so no work was queued there. The task dev shell does not contain strace; I will classify the unchanged proxy test with an exact focused baseline run rather than adding tooling. The live-mesh claim is still J197, whose current build-slot process is active; no mesh state was touched.

Queue update on rebased head `f3af3168118e188dee6d0824ab8cc05cddbfdba9`: the single `verify quick` foreground ticket is still waiting at compute01 build-slot position 5 after about 10 minutes. It has not started Cargo or created `target/`; compute01 has 99 GiB free. dev-02 remains occupied, so no work was queued there. The task dev shell does not contain `strace`; I will classify the unchanged proxy test with an exact focused baseline run rather than adding tooling. The live-mesh claim is still J197, whose current build-slot process is active; no mesh state was touched.
Author
Owner

Queue update on rebased head f3af3168118e188dee6d0824ab8cc05cddbfdba9: the one foreground verify quick ticket has waited about 22 minutes and is now position 3; it has not created a target. compute01 has 82 GiB free (floor 25 GiB). The same command remains active, with no duplicate queued. I will run the focused baseline proxy test only after quick completes and the worktree is safely returned to origin/dev for that check.

Queue update on rebased head `f3af3168118e188dee6d0824ab8cc05cddbfdba9`: the one foreground `verify quick` ticket has waited about 22 minutes and is now position 3; it has not created a target. compute01 has 82 GiB free (floor 25 GiB). The same command remains active, with no duplicate queued. I will run the focused baseline proxy test only after quick completes and the worktree is safely returned to `origin/dev` for that check.
Author
Owner

Queue update: the J254 verify quick ticket remains third in compute01's FIFO after about 26 minutes; no Cargo process or target exists. Free disk has fallen to 58 GiB while other jobs run. I am keeping the single ticket in place; build-slot checks its 25 GiB admission floor before starting. The target remains absent until admission.

Queue update: the J254 `verify quick` ticket remains third in compute01's FIFO after about 26 minutes; no Cargo process or target exists. Free disk has fallen to 58 GiB while other jobs run. I am keeping the single ticket in place; build-slot checks its 25 GiB admission floor before starting. The target remains absent until admission.
Author
Owner

30-minute queue update: the sole J254 verify quick ticket remains third in compute01's FIFO; all 5 slots are occupied, and the command has not started Cargo or created a target. Free disk is 43 GiB. I am preserving the one foreground ticket and monitoring disk before admission. Log: ~/infraternal-ops/j254-evidence/verify-quick-f3.log.

30-minute queue update: the sole J254 `verify quick` ticket remains third in compute01's FIFO; all 5 slots are occupied, and the command has not started Cargo or created a target. Free disk is 43 GiB. I am preserving the one foreground ticket and monitoring disk before admission. Log: `~/infraternal-ops/j254-evidence/verify-quick-f3.log`.
Author
Owner

Live-proof status: compute01 still contains ~/mesh-vm.claim naming J197 from 2026-11-06T22:41:10Z. A current process listing has no J197 process, while ip link show dev infraternal0 still reports the interface present but DOWN. I have not removed the claim or changed the interface because this is not J254-owned state. The two-node live proof remains pending the claim owner's cleanup/confirmation; the current-base gate work continues independently.

Live-proof status: compute01 still contains `~/mesh-vm.claim` naming J197 from `2026-11-06T22:41:10Z`. A current process listing has no J197 process, while `ip link show dev infraternal0` still reports the interface present but DOWN. I have not removed the claim or changed the interface because this is not J254-owned state. The two-node live proof remains pending the claim owner's cleanup/confirmation; the current-base gate work continues independently.
Author
Owner

The J254 verify quick ticket acquired compute01 slot 2 after about 36 minutes and has started Cargo checks at rebased head f3af3168118e188dee6d0824ab8cc05cddbfdba9. Free disk recovered to 89 GiB at admission. The run log is ~/infraternal-ops/j254-evidence/verify-quick-f3.log; the target is being retained for the final fast gate.

The J254 `verify quick` ticket acquired compute01 slot 2 after about 36 minutes and has started Cargo checks at rebased head `f3af3168118e188dee6d0824ab8cc05cddbfdba9`. Free disk recovered to 89 GiB at admission. The run log is `~/infraternal-ops/j254-evidence/verify-quick-f3.log`; the target is being retained for the final fast gate.
Author
Owner

Disk update while the existing quick gate runs: compute01 free space has fallen to 18 GiB (below build-slot's 25 GiB admission floor). The J254 quick ticket was already admitted and continues; its target is 7.9 GiB. I have not queued another gate or deleted the active target. I will wait for this run to finish and check capacity before scheduling the next verification.

Disk update while the existing quick gate runs: compute01 free space has fallen to 18 GiB (below build-slot's 25 GiB admission floor). The J254 quick ticket was already admitted and continues; its target is 7.9 GiB. I have not queued another gate or deleted the active target. I will wait for this run to finish and check capacity before scheduling the next verification.
Author
Owner

Capacity update during the already-admitted J254 quick gate: compute01 has fallen to 6.7 GiB free (99% used) while unrelated jobs run. J254's active target remains 7.9 GiB; its server tests are still progressing. I will not delete the active target or another job's files. No further build will be submitted until the gate exits and disk is back above the build-slot floor.

Capacity update during the already-admitted J254 quick gate: compute01 has fallen to 6.7 GiB free (99% used) while unrelated jobs run. J254's active target remains 7.9 GiB; its server tests are still progressing. I will not delete the active target or another job's files. No further build will be submitted until the gate exits and disk is back above the build-slot floor.
Author
Owner

Capacity update: during the same running quick gate, compute01 now reports 5.1 GiB available at 100% filesystem use. The current server tests continue to pass in the output, and the J254 target has not grown beyond 7.9 GiB. I have started no other build and will let this admitted gate exit before any new work; I am not touching other jobs' targets.

Capacity update: during the same running quick gate, compute01 now reports 5.1 GiB available at 100% filesystem use. The current server tests continue to pass in the output, and the J254 target has not grown beyond 7.9 GiB. I have started no other build and will let this admitted gate exit before any new work; I am not touching other jobs' targets.
Author
Owner

verify quick at rebased head f3af3168118e188dee6d0824ab8cc05cddbfdba9 exited 1 because compute01 ran out of disk, not because of a code diagnostic. The first root error in verify-quick-f3.log is couldn't create a temp dir: No space left on device (os error 28) under target/debug/deps/rmeta*; rustc also reported LLVM output-stream failures and failed .rmeta writes. The filesystem was at 100% with 948 MiB free when the process exited.

I checked the build-slot queue and process list; no J254 command was active or queued. I removed only ~/worktrees/j254-current/target (7.9 GiB), recovering compute01 to 13 GiB free. This remains below the 25 GiB build-slot floor. I have queued no further gate; the current-base quick, fast and privileged verification still need to be completed after capacity recovers.

`verify quick` at rebased head `f3af3168118e188dee6d0824ab8cc05cddbfdba9` exited 1 because compute01 ran out of disk, not because of a code diagnostic. The first root error in `verify-quick-f3.log` is `couldn't create a temp dir: No space left on device (os error 28)` under `target/debug/deps/rmeta*`; rustc also reported LLVM output-stream failures and failed `.rmeta` writes. The filesystem was at 100% with 948 MiB free when the process exited. I checked the build-slot queue and process list; no J254 command was active or queued. I removed only `~/worktrees/j254-current/target` (7.9 GiB), recovering compute01 to 13 GiB free. This remains below the 25 GiB build-slot floor. I have queued no further gate; the current-base quick, fast and privileged verification still need to be completed after capacity recovers.
Author
Owner

After the cleanup, compute01 free space has fallen further to 8.7 GiB. Two build slots are free, but build-slot keeps queued work waiting below its 25 GiB disk floor. The J254 target remains absent and no J254 command is queued. I will not submit a new gate until capacity recovers.

After the cleanup, compute01 free space has fallen further to 8.7 GiB. Two build slots are free, but build-slot keeps queued work waiting below its 25 GiB disk floor. The J254 target remains absent and no J254 command is queued. I will not submit a new gate until capacity recovers.
Author
Owner

Capacity check before retry: compute01 has 70 GiB free, its J254 target is absent, all 5 slots are busy, and 8 jobs are queued. dev-02 has one active job but only 30 GiB free (below its 40 GiB floor); I will not start work there. I am resubmitting scripts/verify quick exactly once on rebased head f3af3168118e188dee6d0824ab8cc05cddbfdba9 through compute01's FIFO build-slot, with log ~/infraternal-ops/j254-evidence/verify-quick-f3-retry.log.

Capacity check before retry: compute01 has 70 GiB free, its J254 target is absent, all 5 slots are busy, and 8 jobs are queued. dev-02 has one active job but only 30 GiB free (below its 40 GiB floor); I will not start work there. I am resubmitting `scripts/verify quick` exactly once on rebased head `f3af3168118e188dee6d0824ab8cc05cddbfdba9` through compute01's FIFO `build-slot`, with log `~/infraternal-ops/j254-evidence/verify-quick-f3-retry.log`.
Author
Owner

The one queued scripts/verify quick retry at rebased head f3af3168118e188dee6d0824ab8cc05cddbfdba9 was cut by the planned compute01 restart before it started. It created no target and has no gate result, so this is an infrastructure interruption, not a test failure. The earlier admitted quick run separately exited with ENOSPC as documented above.

I am resubmitting the cut quick gate exactly once on dev-02, following the 07:40 IST #402 update that restores full gates there and says to use the shorter queue. Before submission, compute01 had five busy slots and eight queued jobs with 68 GiB free; dev-02 had one free slot and 62 GiB free. The dev-02 target will be removed after this run. Log: ~/infraternal-ops/j254-evidence/verify-quick-f3-restart.log.

The one queued `scripts/verify quick` retry at rebased head `f3af3168118e188dee6d0824ab8cc05cddbfdba9` was cut by the planned compute01 restart before it started. It created no target and has no gate result, so this is an infrastructure interruption, not a test failure. The earlier admitted quick run separately exited with ENOSPC as documented above. I am resubmitting the cut quick gate exactly once on dev-02, following the 07:40 IST #402 update that restores full gates there and says to use the shorter queue. Before submission, compute01 had five busy slots and eight queued jobs with 68 GiB free; dev-02 had one free slot and 62 GiB free. The dev-02 target will be removed after this run. Log: `~/infraternal-ops/j254-evidence/verify-quick-f3-restart.log`.
Author
Owner

Current-head scripts/verify quick passed on dev-02 at f3af3168118e188dee6d0824ab8cc05cddbfdba9 with CARGO_INCREMENTAL=0. Verbatim summary: QUICK pre-check passed. This is not the gate: run scripts/verify fast before reporting done. Exit: VERIFY_QUICK_EXIT=0. Artifact: ~/infraternal-ops/j254-evidence/verify-quick-f3-restart.log. I am checking both slots and disk before the separate final fast gate; the same target is retained for this job pending that run.

Current-head `scripts/verify quick` passed on dev-02 at `f3af3168118e188dee6d0824ab8cc05cddbfdba9` with `CARGO_INCREMENTAL=0`. Verbatim summary: `QUICK pre-check passed. This is not the gate: run scripts/verify fast before reporting done.` Exit: `VERIFY_QUICK_EXIT=0`. Artifact: `~/infraternal-ops/j254-evidence/verify-quick-f3-restart.log`. I am checking both slots and disk before the separate final fast gate; the same target is retained for this job pending that run.
Author
Owner

Before the current-head fast gate, I am reproducing the previously observed infraternal-plugin-proxy::supervisor_tests::mesh_grant_revocation_removes_the_route_before_narrowing_its_allowlist NotADirectory failure on the exact current origin/dev baseline fba745051416c60a8523f0fe529c2aee6bbe0dd0. The test source is unchanged from that base, so this focused run will establish whether the prior gate failure is baseline behavior. It is a separate short dev-02 slot run; I will restore f3af316 before starting verify fast. No source changes are planned for this unrelated baseline check.

Before the current-head fast gate, I am reproducing the previously observed `infraternal-plugin-proxy::supervisor_tests::mesh_grant_revocation_removes_the_route_before_narrowing_its_allowlist` `NotADirectory` failure on the exact current `origin/dev` baseline `fba745051416c60a8523f0fe529c2aee6bbe0dd0`. The test source is unchanged from that base, so this focused run will establish whether the prior gate failure is baseline behavior. It is a separate short dev-02 slot run; I will restore `f3af316` before starting `verify fast`. No source changes are planned for this unrelated baseline check.
Author
Owner

The focused proxy test passed on exact origin/dev baseline fba745051416c60a8523f0fe529c2aee6bbe0dd0: 1 passed; 0 failed, exit 0. The test source is identical at f3af316, so the earlier NotADirectory failure in the old-head fast run was not reproduced by this baseline check; I am not attributing it to the J254 diff. Artifact: ~/infraternal-ops/j254-evidence/proxy-baseline-fba745051.log. I am restoring the dev-02 checkout to f3af316 and will run the current-head fast gate next.

The focused proxy test passed on exact `origin/dev` baseline `fba745051416c60a8523f0fe529c2aee6bbe0dd0`: `1 passed; 0 failed`, exit 0. The test source is identical at `f3af316`, so the earlier `NotADirectory` failure in the old-head fast run was not reproduced by this baseline check; I am not attributing it to the J254 diff. Artifact: `~/infraternal-ops/j254-evidence/proxy-baseline-fba745051.log`. I am restoring the dev-02 checkout to `f3af316` and will run the current-head fast gate next.
Author
Owner

Current-head scripts/verify fast is starting on dev-02 at f3af3168118e188dee6d0824ab8cc05cddbfdba9, using the full-gate permission restored in the 07:40 IST #402 update. At admission check dev-02 had one free slot and 53 GiB free (40 GiB floor), with this job's single target at 6.7 GiB; compute01 had five busy slots and seven queued jobs. The fast gate includes the privileged-integration row; its artifact will be ~/infraternal-ops/j254-evidence/verify-fast-f3-restart.log. I will delete the dev-02 target after the gate and report the verbatim summary.

Current-head `scripts/verify fast` is starting on dev-02 at `f3af3168118e188dee6d0824ab8cc05cddbfdba9`, using the full-gate permission restored in the 07:40 IST #402 update. At admission check dev-02 had one free slot and 53 GiB free (40 GiB floor), with this job's single target at 6.7 GiB; compute01 had five busy slots and seven queued jobs. The fast gate includes the privileged-integration row; its artifact will be `~/infraternal-ops/j254-evidence/verify-fast-f3-restart.log`. I will delete the dev-02 target after the gate and report the verbatim summary.
Author
Owner

During the already-admitted current-head verify fast run on dev-02, free space fell from 53 GiB to 25 GiB while the single target grew from 6.7 GiB to 36 GiB. The run is still passing emitted tests and is active; I have not queued other work or touched the active target. I will let this admitted run finish, then delete the target as required. Latest evidence is in ~/infraternal-ops/j254-evidence/verify-fast-f3-restart.log.

During the already-admitted current-head `verify fast` run on dev-02, free space fell from 53 GiB to 25 GiB while the single target grew from 6.7 GiB to 36 GiB. The run is still passing emitted tests and is active; I have not queued other work or touched the active target. I will let this admitted run finish, then delete the target as required. Latest evidence is in `~/infraternal-ops/j254-evidence/verify-fast-f3-restart.log`.
Author
Owner

The current-head verify fast run on dev-02 reached the SSH install suite and reported one failure: streaming::decoded_image_and_hash_advance_before_the_transfer_tail_is_released timed out at crates/plugins/ssh/tests/install/streaming.rs:114, with hashed=false, staged_bytes=0, input_progress=237568, and expected_bytes=624481. This is outside the J254 diff; the fast gate continues through its remaining rows. I am not changing code while the gate is active. Artifact: ~/infraternal-ops/j254-evidence/verify-fast-f3-restart.log.

The current-head `verify fast` run on dev-02 reached the SSH install suite and reported one failure: `streaming::decoded_image_and_hash_advance_before_the_transfer_tail_is_released` timed out at `crates/plugins/ssh/tests/install/streaming.rs:114`, with `hashed=false`, `staged_bytes=0`, `input_progress=237568`, and `expected_bytes=624481`. This is outside the J254 diff; the fast gate continues through its remaining rows. I am not changing code while the gate is active. Artifact: `~/infraternal-ops/j254-evidence/verify-fast-f3-restart.log`.
Author
Owner

The privileged-integration portion of current-head verify fast reported another failure: infraternal-plugin-mesh test:authorized_local_binding nf_tables_local_binding_and_address_recovery failed at crates/plugins/mesh/tests/authorized_local_binding.rs:41 when spawning ip returned NotFound; the paired nft_local_binding_and_address_recovery run passed. Root cause is not yet established. Dev-02 currently has 18 GiB free and the active target is 44 GiB; the admitted gate is still running. I have submitted no other work and will not remove its target. The full log remains ~/infraternal-ops/j254-evidence/verify-fast-f3-restart.log.

The privileged-integration portion of current-head `verify fast` reported another failure: `infraternal-plugin-mesh test:authorized_local_binding nf_tables_local_binding_and_address_recovery` failed at `crates/plugins/mesh/tests/authorized_local_binding.rs:41` when spawning `ip` returned `NotFound`; the paired `nft_local_binding_and_address_recovery` run passed. Root cause is not yet established. Dev-02 currently has 18 GiB free and the active target is 44 GiB; the admitted gate is still running. I have submitted no other work and will not remove its target. The full log remains `~/infraternal-ops/j254-evidence/verify-fast-f3-restart.log`.
Author
Owner

The current-head fast gate has reached static builds and cross-compiled the x86_64 musl binary on dev-02. Reported size is 28,060,912 bytes versus 22,418,256 bytes at the base, delta +5,642,656 bytes. The x86 runtime AES-GCM vector and key-memory probes correctly report SKIP on the aarch64 host. The log is still active at ~/infraternal-ops/j254-evidence/verify-fast-f3-restart.log; it is now building the aarch64 static binary. Disk is 17 GiB free with one 45 GiB target, and I am monitoring the admitted run.

The current-head fast gate has reached static builds and cross-compiled the x86_64 musl binary on dev-02. Reported size is 28,060,912 bytes versus 22,418,256 bytes at the base, delta +5,642,656 bytes. The x86 runtime AES-GCM vector and key-memory probes correctly report SKIP on the aarch64 host. The log is still active at `~/infraternal-ops/j254-evidence/verify-fast-f3-restart.log`; it is now building the aarch64 static binary. Disk is 17 GiB free with one 45 GiB target, and I am monitoring the admitted run.
Author
Owner

Update to the in-progress gate note above: scripts/verify fast completed on dev-02 at f3af3168118e188dee6d0824ab8cc05cddbfdba9 with exit 1. The ordinary test row reported 104 passed, 1 failed, 2 ignored; the failure was streaming::decoded_image_and_hash_advance_before_the_transfer_tail_is_released (hashed=false, input_progress=237568, staged bytes 0). The SSH source is unchanged from origin/dev, so I am not attributing this to the J254 diff.

The privileged row reported 18 failures. Dev-02 lacks /usr/sbin/iptables-nft, which explains the nf_tables backend failures; the separate nft_ssh counter lookup ENOENT remains unclassified. Static builds passed: x86_64 musl 28,060,912 bytes vs 22,418,256 at base (+5,642,656); aarch64 musl 25,229,704 vs 20,453,280 (+4,776,424). Runtime probes that require x86 execution skipped on aarch64; key-memory checks passed.

The dev-02 target was removed after the run and free space returned to 62 GiB. Log: ~/infraternal-ops/j254-evidence/verify-fast-f3-restart.log. I will add the verbatim gate summary after recovering that log. The DNS latency/memory measurement and remaining Linux live proof are still pending.

Update to the in-progress gate note above: `scripts/verify fast` completed on dev-02 at `f3af3168118e188dee6d0824ab8cc05cddbfdba9` with exit 1. The ordinary test row reported 104 passed, 1 failed, 2 ignored; the failure was `streaming::decoded_image_and_hash_advance_before_the_transfer_tail_is_released` (`hashed=false`, `input_progress=237568`, staged bytes 0). The SSH source is unchanged from `origin/dev`, so I am not attributing this to the J254 diff. The privileged row reported 18 failures. Dev-02 lacks `/usr/sbin/iptables-nft`, which explains the `nf_tables` backend failures; the separate `nft_ssh` counter lookup ENOENT remains unclassified. Static builds passed: x86_64 musl 28,060,912 bytes vs 22,418,256 at base (+5,642,656); aarch64 musl 25,229,704 vs 20,453,280 (+4,776,424). Runtime probes that require x86 execution skipped on aarch64; key-memory checks passed. The dev-02 target was removed after the run and free space returned to 62 GiB. Log: `~/infraternal-ops/j254-evidence/verify-fast-f3-restart.log`. I will add the verbatim gate summary after recovering that log. The DNS latency/memory measurement and remaining Linux live proof are still pending.
Author
Owner

D327/D339 and resolver-integration source audit at f3af316: no further code gap found. For nonlocal query sources, response_for_source returns through negative_response before looking up the label; a nonexistent label from that source takes the same branch and emits the same NXDOMAIN/SOA path. denied_remote_names_match_the_nonexistent_name_response exercises the UDP boundary and compares the DNS response sections. The name snapshot contains only configured real names; no unnamed-ID projection is added.

Resolver routing is scoped as requested: Linux prefers a per-link resolvectl route with default-route no, then falls back only to NetworkManager dnsmasq when it manages infraternal0 and the node owns port 53. If neither split-DNS mechanism is available, existing host DNS remains untouched. On macOS, root nodes write marker-owned /etc/resolver/<suffix> files to the mesh listener's high port; userspace nodes leave system resolver files alone, and cleanup removes only marked files. This is source/test evidence; Linux real-machine resolution and removal proof remain pending.

D327/D339 and resolver-integration source audit at `f3af316`: no further code gap found. For nonlocal query sources, `response_for_source` returns through `negative_response` before looking up the label; a nonexistent label from that source takes the same branch and emits the same NXDOMAIN/SOA path. `denied_remote_names_match_the_nonexistent_name_response` exercises the UDP boundary and compares the DNS response sections. The name snapshot contains only configured real names; no unnamed-ID projection is added. Resolver routing is scoped as requested: Linux prefers a per-link `resolvectl` route with `default-route no`, then falls back only to NetworkManager dnsmasq when it manages `infraternal0` and the node owns port 53. If neither split-DNS mechanism is available, existing host DNS remains untouched. On macOS, root nodes write marker-owned `/etc/resolver/<suffix>` files to the mesh listener's high port; userspace nodes leave system resolver files alone, and cleanup removes only marked files. This is source/test evidence; Linux real-machine resolution and removal proof remain pending.
Author
Owner

Correction to the gate summary above: the earlier detailed #266 progress note records Node::restore failing because it could not spawn ip (NotFound). The branch also hardcodes /usr/sbin/iptables-nft for its nf_tables backend, and the handoff record says that binary was missing on dev-02, but I cannot map the 18 failures to those missing tools without the complete log. Treat the prior iptables-nft attribution as provisional; I will replace it with the log-backed breakdown after recovery. The separate nft_ssh counter lookup ENOENT also remains unresolved.

Correction to the gate summary above: the earlier detailed #266 progress note records `Node::restore` failing because it could not spawn `ip` (`NotFound`). The branch also hardcodes `/usr/sbin/iptables-nft` for its `nf_tables` backend, and the handoff record says that binary was missing on dev-02, but I cannot map the 18 failures to those missing tools without the complete log. Treat the prior `iptables-nft` attribution as provisional; I will replace it with the log-backed breakdown after recovery. The separate `nft_ssh` counter lookup ENOENT also remains unresolved.
Author
Owner

J254 maintenance hold at f3af3168118e188dee6d0824ab8cc05cddbfdba9 (mesh-dns-j254): compute01 admission is frozen for the planned restart. I have not queued or started a compute01 gate during this hold.

The macOS resolver work and the locked Mac build/test checks are already complete, with resolver install/removal receipts recorded above. Existing DNS load evidence on #266 reports p50 75 µs, p99 90 µs, and 135,168 bytes per 1,000 names. The current-head verify quick passed on dev-02; its verify fast completed with exit 1, and its test/privileged breakdown is recorded above. The previous compute01 waiter cut by restart is recorded separately from that completed dev-02 run.

Still pending: Linux live proof across compute01 and dev-02 (ping <name>.internal, ssh <name>, denied-name versus nonexistent-name byte-identical NXDOMAIN/SOA, and resolver cleanup), plus the required foreground scripts/verify fast after the explicit #402 “compute01 back, 5 slots” notice. I will wait for that notice before submitting the gate. I have not changed live mesh or resolver state during this hold.

J254 maintenance hold at `f3af3168118e188dee6d0824ab8cc05cddbfdba9` (`mesh-dns-j254`): compute01 admission is frozen for the planned restart. I have not queued or started a compute01 gate during this hold. The macOS resolver work and the locked Mac build/test checks are already complete, with resolver install/removal receipts recorded above. Existing DNS load evidence on #266 reports p50 75 µs, p99 90 µs, and 135,168 bytes per 1,000 names. The current-head `verify quick` passed on dev-02; its `verify fast` completed with exit 1, and its test/privileged breakdown is recorded above. The previous compute01 waiter cut by restart is recorded separately from that completed dev-02 run. Still pending: Linux live proof across compute01 and dev-02 (`ping <name>.internal`, `ssh <name>`, denied-name versus nonexistent-name byte-identical NXDOMAIN/SOA, and resolver cleanup), plus the required foreground `scripts/verify fast` after the explicit #402 “compute01 back, 5 slots” notice. I will wait for that notice before submitting the gate. I have not changed live mesh or resolver state during this hold.
Author
Owner

J254 current-base update: fjweb/dev advanced from fba745051416c60a8523f0fe529c2aee6bbe0dd0 to 3644950b8e4b5dddeefba350f97a3892189ca193 (four commits). I rebased the nine J254 commits cleanly onto the new base and pushed the branch with a lease pinned to the prior remote head.

The branch is now mesh-dns-j254 at 726ee694f3b09fc9268b3fe24cb2ede6635c3b55, verified on the SSH Git remote; it is nine commits ahead of current dev, and the worktree is clean. No build or gate has run on this rebased head because compute01 remains under the maintenance hold. I will submit the required foreground scripts/verify fast once after the explicit #402 “compute01 back, 5 slots” notice. Linux two-node resolver and cleanup proof remains outstanding.

J254 current-base update: `fjweb/dev` advanced from `fba745051416c60a8523f0fe529c2aee6bbe0dd0` to `3644950b8e4b5dddeefba350f97a3892189ca193` (four commits). I rebased the nine J254 commits cleanly onto the new base and pushed the branch with a lease pinned to the prior remote head. The branch is now `mesh-dns-j254` at `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`, verified on the SSH Git remote; it is nine commits ahead of current `dev`, and the worktree is clean. No build or gate has run on this rebased head because compute01 remains under the maintenance hold. I will submit the required foreground `scripts/verify fast` once after the explicit #402 “compute01 back, 5 slots” notice. Linux two-node resolver and cleanup proof remains outstanding.
Author
Owner

Correction to my previous maintenance status at f3af316: kayg confirmed on #402 that no further compute01 restart is planned. The only restart was 02:15–02:45 IST; admission has been open at five slots since then (#402 comment 83012 and the 12:00 IST update). My earlier note saying I was waiting for a future back notice is stale. I have resumed J254.

The current branch is rebased and pushed at 726ee694f3b09fc9268b3fe24cb2ede6635c3b55. The compute01 build-slot has five busy jobs and seven queued jobs, with 53 GiB free; it contains no J254 queue entry. The clean J254 compute worktree is at this head with a 12 GiB target. Dev-02 has one busy job and two queued; its verify fast remains excluded as the merge gate because of the nf_tables environment gap J341 is investigating.

I am refreshing origin/dev and will run verify quick through compute01's build-slot, then the required verify fast there using the same target. The Linux live proof remains pending J328's shared mesh claim; I have not touched that state.

Correction to my previous maintenance status at `f3af316`: kayg confirmed on #402 that no further compute01 restart is planned. The only restart was 02:15–02:45 IST; admission has been open at five slots since then (#402 comment 83012 and the 12:00 IST update). My earlier note saying I was waiting for a future back notice is stale. I have resumed J254. The current branch is rebased and pushed at `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`. The compute01 build-slot has five busy jobs and seven queued jobs, with 53 GiB free; it contains no J254 queue entry. The clean J254 compute worktree is at this head with a 12 GiB target. Dev-02 has one busy job and two queued; its `verify fast` remains excluded as the merge gate because of the nf_tables environment gap J341 is investigating. I am refreshing `origin/dev` and will run `verify quick` through compute01's build-slot, then the required `verify fast` there using the same target. The Linux live proof remains pending J328's shared mesh claim; I have not touched that state.
Author
Owner

Recovered the prior dev-02 verify fast log at /home/claude/infraternal-ops/j254-evidence/verify-fast-f3-restart.log (run was on pre-rebase head f3af3168118e188dee6d0824ab8cc05cddbfdba9). Its verbatim gate table reported test FAIL (101), privileged integration FAIL (1), static builds PASS, and VERIFY_FAST_EXIT=1.

The ordinary test row was 104 passed; 1 failed; 2 ignored. Its failure was streaming::decoded_image_and_hash_advance_before_the_transfer_tail_is_released: hashed=false, release NotFound, input_progress=237568, staged_bytes=0, expected_bytes=624481.

The privileged log lists 18 failed cases: 17 nf_tables_* cases, including authorized_local_binding::nf_tables_local_binding_and_address_recovery failing with NotFound at tests/authorized_local_binding.rs:41, plus nft_ssh_checks_the_original_service_after_translation, whose list counter inet ssh_port_test delivered lookup returned No such file or directory. The paired nft_local_binding_and_address_recovery case passed. This is consistent with the dev-02 environment gap kayg described; I am leaving root-cause attribution to J341 and have made no code change for it.

The same log reports x86_64 static size 28,060,912 bytes (+5,642,656 vs base) and aarch64 25,229,704 bytes (+4,776,424 vs base); the x86 runtime probes correctly skipped on aarch64. The rebased current-head gate is still pending on compute01.

Recovered the prior dev-02 `verify fast` log at `/home/claude/infraternal-ops/j254-evidence/verify-fast-f3-restart.log` (run was on pre-rebase head `f3af3168118e188dee6d0824ab8cc05cddbfdba9`). Its verbatim gate table reported `test FAIL (101)`, `privileged integration FAIL (1)`, `static builds PASS`, and `VERIFY_FAST_EXIT=1`. The ordinary test row was `104 passed; 1 failed; 2 ignored`. Its failure was `streaming::decoded_image_and_hash_advance_before_the_transfer_tail_is_released`: `hashed=false`, release `NotFound`, `input_progress=237568`, `staged_bytes=0`, `expected_bytes=624481`. The privileged log lists 18 failed cases: 17 `nf_tables_*` cases, including `authorized_local_binding::nf_tables_local_binding_and_address_recovery` failing with `NotFound` at `tests/authorized_local_binding.rs:41`, plus `nft_ssh_checks_the_original_service_after_translation`, whose `list counter inet ssh_port_test delivered` lookup returned `No such file or directory`. The paired `nft_local_binding_and_address_recovery` case passed. This is consistent with the dev-02 environment gap kayg described; I am leaving root-cause attribution to J341 and have made no code change for it. The same log reports x86_64 static size 28,060,912 bytes (+5,642,656 vs base) and aarch64 25,229,704 bytes (+4,776,424 vs base); the x86 runtime probes correctly skipped on aarch64. The rebased current-head gate is still pending on compute01.
Author
Owner

J254 queue update at 726ee694f3b09fc9268b3fe24cb2ede6635c3b55: the single foreground verify quick ticket is PID 3262772, queue position 5 after about 28 minutes. All five compute01 slots remain busy; free disk is 108 GiB (25 GiB floor), and the existing 12 GiB J254 target is retained for the queued command. Cargo has not started and there is no test result. I have not submitted a duplicate ticket. After quick exits, I will run verify fast on compute01 using this target.

J254 queue update at `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`: the single foreground `verify quick` ticket is PID 3262772, queue position 5 after about 28 minutes. All five compute01 slots remain busy; free disk is 108 GiB (25 GiB floor), and the existing 12 GiB J254 target is retained for the queued command. Cargo has not started and there is no test result. I have not submitted a duplicate ticket. After quick exits, I will run `verify fast` on compute01 using this target.
Author
Owner

J254 queue update at 726ee694f3b09fc9268b3fe24cb2ede6635c3b55: the same foreground verify quick ticket, PID 3262772, has advanced to position 3 after about 31 minutes. Compute01 has 5 occupied slots and 94 GiB free, above the 25 GiB floor. Cargo and tests have not started; this is still a scheduling wait, and no duplicate was submitted. The existing J254 target and evidence log path remain ~/worktrees/j254-current/target and ~/infraternal-ops/j254-evidence/verify-quick-726.log.

J254 queue update at `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`: the same foreground `verify quick` ticket, PID 3262772, has advanced to position 3 after about 31 minutes. Compute01 has 5 occupied slots and 94 GiB free, above the 25 GiB floor. Cargo and tests have not started; this is still a scheduling wait, and no duplicate was submitted. The existing J254 target and evidence log path remain `~/worktrees/j254-current/target` and `~/infraternal-ops/j254-evidence/verify-quick-726.log`.
Author
Owner

J254 quick command invocation finding at head 726ee694f3b09fc9268b3fe24cb2ede6635c3b55: the foreground ticket (PID 3262772) acquired compute01 slot 4 at 08:18:40Z, then exited 127 before Cargo. Log /home/claude/infraternal-ops/j254-evidence/verify-quick-726.log ends with /home/claude/scripts/verify: No such file or directory. nix develop <worktree> set the shell environment but kept the remote working directory at /home/claude, so the relative script path was wrong. This is a command invocation error, not a test result; the J254 target remains 12 GiB and the worktree is clean. I will recheck Cargo in the dev shell and resubmit quick from the worktree directory. verify fast has not run on the rebased head.

J254 quick command invocation finding at head `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`: the foreground ticket (PID 3262772) acquired compute01 slot 4 at 08:18:40Z, then exited 127 before Cargo. Log `/home/claude/infraternal-ops/j254-evidence/verify-quick-726.log` ends with `/home/claude/scripts/verify: No such file or directory`. `nix develop <worktree>` set the shell environment but kept the remote working directory at `/home/claude`, so the relative script path was wrong. This is a command invocation error, not a test result; the J254 target remains 12 GiB and the worktree is clean. I will recheck Cargo in the dev shell and resubmit quick from the worktree directory. `verify fast` has not run on the rebased head.
Author
Owner

J254 current-head verification update: branch mesh-dns-j254 is pushed at 726ee694f3b09fc9268bfe24cb2ede6635c3b55 (rebased on dev 3644950b8e4b5dddeefba350f97a3892189ca193).

verify quick passed on compute01: server suite 556 passed, 0 failed, 26 ignored; bin-only suite 216 passed, 0 failed, 15 ignored. Log: /home/claude/infraternal-ops/j254-evidence/verify-quick-726-nix.log.

The required foreground scripts/verify fast also completed on compute01 at this head with exit 0. Verbatim Rust gate summary:

| Rust gate                 | Result       |
+---------------------------+--------------+
| Nix Cargo vendoring       | PASS         |
| fmt                       | PASS         |
| migration names           | PASS         |
| migration tooling tests   | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| real-binary fixture paths | PASS         |
| Traefik component manifest | PASS         |
| release component publication | PASS         |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| doc coverage ratchet      | PASS         |
| shape lints off (D351)    | PASS         |
| doc comments on diff      | PASS         |
| fixed test waits          | PASS         |
| privileged integration    | PASS         |
| probe release exclusion   | PASS         |
| nightly fuzz reporter     | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+

All 96 privileged integration cases passed. TypeScript protocol tests passed: 50 pass, 0 fail, 77,253 expectations. Fast log: /home/claude/infraternal-ops/j254-evidence/verify-fast-726.log.

Static size evidence from that run: x86_64 musl 28,058,672 bytes (+5,640,416 vs the 22,418,256-byte baseline); aarch64 musl 25,227,176 bytes (+4,773,896 vs the 20,453,280-byte baseline). Existing perf-VM evidence remains 80,000 queries / 8 clients, p50 75 µs, p99 90 µs, and +135,168 bytes RSS per 1,000 names.

Remaining evidence: Linux two-node live resolution/SSH, denied-name byte equality, and resolver cleanup. I will recheck the shared mesh claim before touching mesh state; dev-02's ip/Node::restore environment failure remains under J341 investigation.

J254 current-head verification update: branch `mesh-dns-j254` is pushed at `726ee694f3b09fc9268bfe24cb2ede6635c3b55` (rebased on `dev` `3644950b8e4b5dddeefba350f97a3892189ca193`). `verify quick` passed on compute01: server suite 556 passed, 0 failed, 26 ignored; bin-only suite 216 passed, 0 failed, 15 ignored. Log: `/home/claude/infraternal-ops/j254-evidence/verify-quick-726-nix.log`. The required foreground `scripts/verify fast` also completed on compute01 at this head with exit 0. Verbatim Rust gate summary: ``` | Rust gate | Result | +---------------------------+--------------+ | Nix Cargo vendoring | PASS | | fmt | PASS | | migration names | PASS | | migration tooling tests | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | Traefik component manifest | PASS | | release component publication | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | doc coverage ratchet | PASS | | shape lints off (D351) | PASS | | doc comments on diff | PASS | | fixed test waits | PASS | | privileged integration | PASS | | probe release exclusion | PASS | | nightly fuzz reporter | PASS | | static builds | PASS | +---------------------------+--------------+ ``` All 96 privileged integration cases passed. TypeScript protocol tests passed: 50 pass, 0 fail, 77,253 expectations. Fast log: `/home/claude/infraternal-ops/j254-evidence/verify-fast-726.log`. Static size evidence from that run: x86_64 musl 28,058,672 bytes (+5,640,416 vs the 22,418,256-byte baseline); aarch64 musl 25,227,176 bytes (+4,773,896 vs the 20,453,280-byte baseline). Existing perf-VM evidence remains 80,000 queries / 8 clients, p50 75 µs, p99 90 µs, and +135,168 bytes RSS per 1,000 names. Remaining evidence: Linux two-node live resolution/SSH, denied-name byte equality, and resolver cleanup. I will recheck the shared mesh claim before touching mesh state; dev-02's `ip`/`Node::restore` environment failure remains under J341 investigation.
Author
Owner

J254 Linux live-proof recheck at 2026-11-07T10:55:38Z: compute01's shared ~/mesh-vm.claim currently reads J207 2026-11-07T06:55:47Z. infraternal0 is DOWN with the existing mesh addresses still attached; systemctl is-active infraternal reports inactive. The process inventory has PIDs 998, 3938718, and 3938755 named infraternal, so I have not treated the claim as stale or changed the interface/resolver. No Linux live resolution/SSH/cleanup proof was run. The two-node live proof remains pending the shared claim clearing; dev-02's ip/Node::restore failure also remains with J341 for environment investigation.

J254 Linux live-proof recheck at `2026-11-07T10:55:38Z`: compute01's shared `~/mesh-vm.claim` currently reads `J207 2026-11-07T06:55:47Z`. `infraternal0` is DOWN with the existing mesh addresses still attached; `systemctl is-active infraternal` reports `inactive`. The process inventory has PIDs 998, 3938718, and 3938755 named `infraternal`, so I have not treated the claim as stale or changed the interface/resolver. No Linux live resolution/SSH/cleanup proof was run. The two-node live proof remains pending the shared claim clearing; dev-02's `ip`/`Node::restore` failure also remains with J341 for environment investigation.
Author
Owner

After the successful fast gate, I checked that no process had a working directory under j254-current, then removed only /home/claude/worktrees/j254-current/target as requested for compute01 disk headroom. It was 64 GiB; free space is now 111 GiB. The branch remains clean at 726ee694f3b09fc9268b3fe24cb2ede6635c3b55. No shared mesh or resolver state was changed.

After the successful fast gate, I checked that no process had a working directory under `j254-current`, then removed only `/home/claude/worktrees/j254-current/target` as requested for compute01 disk headroom. It was 64 GiB; free space is now 111 GiB. The branch remains clean at `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`. No shared mesh or resolver state was changed.
Author
Owner

J254 read-only dev-02 state check at 2026-11-07T11:10:07Z: command -v ip resolves to /usr/sbin/ip; infraternal0 is absent, no infraternal process is running, and the host has 84 GiB free. I ran no build, test, or sync there. This confirms the host has iproute2, but does not establish the PATH used by the earlier Node::restore process; J341's environment investigation remains authoritative.

J254 read-only dev-02 state check at `2026-11-07T11:10:07Z`: `command -v ip` resolves to `/usr/sbin/ip`; `infraternal0` is absent, no `infraternal` process is running, and the host has 84 GiB free. I ran no build, test, or sync there. This confirms the host has `iproute2`, but does not establish the PATH used by the earlier `Node::restore` process; J341's environment investigation remains authoritative.
Author
Owner

J254 current-tree audit at 726ee694f3b09fc9268b3fe24cb2ede6635c3b55 confirms the code-level DNS requirements:

  • response_for_source rejects a non-local mesh source before looking up a label and calls the same negative_response helper used for a missing local name. Both return NXDOMAIN with the same SOA through the local listener; the denied-source branch does not fall through to upstream. The UDP boundary regression sends a denied and nonexistent name from the same peer and compares the DNS response sections byte-for-byte (excluding the differing question), then confirms the node's own allowed view resolves the reachable name.
  • DNS records are built only from accepted peer_names; no unnamed-ID projection is added.
  • Linux prefers resolvectl with default-route no, a per-link DNS server and configured suffix domains. The NetworkManager fallback requires dnsmasq, a managed infraternal0, and the node's port-53 listener. Stop cleanup targets only that link; unsupported hosts leave host DNS unchanged.
  • Root macOS nodes write marker-owned /etc/resolver/<suffix> files for the high-port mesh listener and refresh the resolver. Userspace nodes leave system files alone. Mac smoke receipts already recorded above prove install, lookup, cleanup, and userspace no-write behavior.

This source/test audit does not substitute for the requested Linux two-VM runtime proof. That remains pending the J207 shared mesh claim clearing.

J254 current-tree audit at `726ee694f3b09fc9268b3fe24cb2ede6635c3b55` confirms the code-level DNS requirements: - `response_for_source` rejects a non-local mesh source before looking up a label and calls the same `negative_response` helper used for a missing local name. Both return NXDOMAIN with the same SOA through the local listener; the denied-source branch does not fall through to upstream. The UDP boundary regression sends a denied and nonexistent name from the same peer and compares the DNS response sections byte-for-byte (excluding the differing question), then confirms the node's own allowed view resolves the reachable name. - DNS records are built only from accepted `peer_names`; no unnamed-ID projection is added. - Linux prefers `resolvectl` with `default-route no`, a per-link DNS server and configured suffix domains. The `NetworkManager` fallback requires dnsmasq, a managed `infraternal0`, and the node's port-53 listener. Stop cleanup targets only that link; unsupported hosts leave host DNS unchanged. - Root macOS nodes write marker-owned `/etc/resolver/<suffix>` files for the high-port mesh listener and refresh the resolver. Userspace nodes leave system files alone. Mac smoke receipts already recorded above prove install, lookup, cleanup, and userspace no-write behavior. This source/test audit does not substitute for the requested Linux two-VM runtime proof. That remains pending the J207 shared mesh claim clearing.
Author
Owner

J254 live-proof recheck at 2026-11-07T11:14:25Z: ~/mesh-vm.claim still names J207 (2026-11-07T06:55:47Z); PIDs 998, 3938718, and 3938755 are still present as infraternal; infraternal0 remains DOWN and the service is inactive. The pushed branch is unchanged and clean at 726ee694f3b09fc9268b3fe24cb2ede6635c3b55; the J254 target remains absent. I made no mesh or resolver changes. Linux live proof is still waiting on release of the shared claim or an alternate free pair.

J254 live-proof recheck at `2026-11-07T11:14:25Z`: `~/mesh-vm.claim` still names J207 (`2026-11-07T06:55:47Z`); PIDs 998, 3938718, and 3938755 are still present as `infraternal`; `infraternal0` remains DOWN and the service is inactive. The pushed branch is unchanged and clean at `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`; the J254 target remains absent. I made no mesh or resolver changes. Linux live proof is still waiting on release of the shared claim or an alternate free pair.
Author
Owner

J254 live-proof wait update at 2026-11-07T11:23:33Z: the shared claim no longer names J207; it now reads J328 2026-11-07T11:23:33Z. My atomic noclobber claim attempt failed, so I left it untouched. infraternal0 is still DOWN. Both prebuilt kernel_node fixtures and the temporary dev-02 coordinator fixture remain in the evidence directory; the current branch has no changes under crates/plugins/mesh since the fixture build head 65f44579312b5a34d330135b79920440bb8cb556. No build, sync, or mesh-state change was made. I will retry the claim in the foreground when the file is absent.

J254 live-proof wait update at `2026-11-07T11:23:33Z`: the shared claim no longer names J207; it now reads `J328 2026-11-07T11:23:33Z`. My atomic `noclobber` claim attempt failed, so I left it untouched. `infraternal0` is still DOWN. Both prebuilt `kernel_node` fixtures and the temporary dev-02 coordinator fixture remain in the evidence directory; the current branch has no changes under `crates/plugins/mesh` since the fixture build head `65f44579312b5a34d330135b79920440bb8cb556`. No build, sync, or mesh-state change was made. I will retry the claim in the foreground when the file is absent.
Author
Owner

J254 live-proof blocker (2026-11-07 11:50:52 UTC), branch mesh-dns-j254 at 726ee694f3b09fc9268b3fe24cb2ede6635c3b55:

  • The prior handoff left my own claim, J254 2026-11-07T11:33:08Z, in ~/mesh-vm.claim. No live step had started. I removed only that matching claim; the file is now free.
  • Read-only inspection shows compute01's infraternal0 is a root-owned GotaTun link with the old mesh addresses and no DNS scope. PID 998, running as j93fixture from /home/j93fixture/.local/share/infraternal/bin/live, is still present. The J207 branch-server processes (PIDs 3938718 and 3938755) are also still running from j207-evidence. dev-02 currently has no infraternal0 or infraternal process.
  • The current-head verify quick, verify fast, and privileged row receipts are already recorded above. I made no build, sync, or mesh/resolver changes in this continuation.

BRIEF-COMMON.md says not to delete an interface or state directory we did not create. I will not replace or clean up the J93-owned interface or stop the J207 processes. Please have their owner/orchestrator confirm the link and processes are released and clean them up, or confirm a safe handoff. Once the pair is clear, I can continue the short Linux proof on compute01 + dev-02 and release the claim immediately afterwards.

J254 live-proof blocker (2026-11-07 11:50:52 UTC), branch `mesh-dns-j254` at `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`: - The prior handoff left my own claim, `J254 2026-11-07T11:33:08Z`, in `~/mesh-vm.claim`. No live step had started. I removed only that matching claim; the file is now free. - Read-only inspection shows compute01's `infraternal0` is a root-owned GotaTun link with the old mesh addresses and no DNS scope. PID 998, running as `j93fixture` from `/home/j93fixture/.local/share/infraternal/bin/live`, is still present. The J207 branch-server processes (PIDs 3938718 and 3938755) are also still running from `j207-evidence`. dev-02 currently has no `infraternal0` or infraternal process. - The current-head `verify quick`, `verify fast`, and privileged row receipts are already recorded above. I made no build, sync, or mesh/resolver changes in this continuation. `BRIEF-COMMON.md` says not to delete an interface or state directory we did not create. I will not replace or clean up the J93-owned interface or stop the J207 processes. Please have their owner/orchestrator confirm the link and processes are released and clean them up, or confirm a safe handoff. Once the pair is clear, I can continue the short Linux proof on compute01 + dev-02 and release the claim immediately afterwards.
Author
Owner

J254 continuation (2026-11-07 12:07:09 UTC) at pushed head 726ee694f3b09fc9268b3fe24cb2ede6635c3b55:

The shared claim file currently reads FREE. J207's server PID 3938718 was absent at the last check; I will verify again immediately before the live run. The x86 and aarch64 kernel_node fixtures remain in the evidence directory. I could not find the retained D339 coordinator fixture on either VM, so I will build only the coordinator example on compute01 through build-slot before taking the live-mesh claim. No mesh or resolver state has been changed.

J254 continuation (2026-11-07 12:07:09 UTC) at pushed head `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`: The shared claim file currently reads FREE. J207's server PID 3938718 was absent at the last check; I will verify again immediately before the live run. The x86 and aarch64 `kernel_node` fixtures remain in the evidence directory. I could not find the retained D339 coordinator fixture on either VM, so I will build only the coordinator example on compute01 through build-slot before taking the live-mesh claim. No mesh or resolver state has been changed.
Author
Owner

J254 Linux proof progress: acquired ~/mesh-vm.claim at 2026-11-07T12:29:07Z. The requested root before-capture returned exactly Device "infraternal0" does not exist. on compute01, so no delete was issued; I will capture again after node cleanup for the after state. J207 PID 3938718 and port 45680 were absent. dev-02 also has no infraternal0. Continuing with the two-node proof.

J254 Linux proof progress: acquired `~/mesh-vm.claim` at 2026-11-07T12:29:07Z. The requested root before-capture returned exactly `Device "infraternal0" does not exist.` on compute01, so no delete was issued; I will capture again after node cleanup for the after state. J207 PID 3938718 and port 45680 were absent. dev-02 also has no `infraternal0`. Continuing with the two-node proof.
Author
Owner

Linux proof finding: coordinator-shape.json from the temporary fixture has no name or peer_names. The fixture calls the store directly and skips production coordinator identity negotiation (dns_names); node_coordination.rs only forwards those fields to negotiated nodes. I will add the negotiated DNS fields to the throwaway proof chunks, with the actual VM hostnames and wt0 endpoints, and leave repository code unchanged.

Linux proof finding: `coordinator-shape.json` from the temporary fixture has no `name` or `peer_names`. The fixture calls the store directly and skips production coordinator identity negotiation (`dns_names`); `node_coordination.rs` only forwards those fields to negotiated nodes. I will add the negotiated DNS fields to the throwaway proof chunks, with the actual VM hostnames and `wt0` endpoints, and leave repository code unchanged.
Author
Owner

Linux proof harness finding: the first ssh -T node launch closed stdin, so the compute01 fixture exited after creating a persistent GotaTun interface. I captured ip -d link: infraternal0 had tun type tun, alias infraternal.mesh.gotatun.v2:fe97fead…, and no process held /dev/net/tun except tailscaled; I removed that fixture-owned interface and confirmed Device "infraternal0" does not exist. The first dev-02 launch returned Error: Os { code: 2, kind: NotFound }; root on dev-02 has /usr/sbin/ip, /usr/sbin/nft, and /dev/net/tun. Retrying with persistent stdin sessions and root access through cua on dev-02.

Linux proof harness finding: the first `ssh -T` node launch closed stdin, so the compute01 fixture exited after creating a persistent GotaTun interface. I captured `ip -d link`: `infraternal0` had `tun type tun`, alias `infraternal.mesh.gotatun.v2:fe97fead…`, and no process held `/dev/net/tun` except tailscaled; I removed that fixture-owned interface and confirmed `Device "infraternal0" does not exist.` The first dev-02 launch returned `Error: Os { code: 2, kind: NotFound }`; root on dev-02 has `/usr/sbin/ip`, `/usr/sbin/nft`, and `/dev/net/tun`. Retrying with persistent stdin sessions and root access through `cua` on dev-02.
Author
Owner

Linux proof update: dev-02 returns Error: Os { code: 2, kind: NotFound } when the fixture is started with --nf-tables, but the same aarch64 fixture starts and reports dns_names:true with its default firewall. /usr/sbin/ip, /usr/sbin/nft, and /dev/net/tun are present. This matches the known dev-02 privileged nf_tables environment gap; I am using the default firewall there for DNS, while the privileged gate evidence remains the compute01 pass at 726ee694.

Linux proof update: dev-02 returns `Error: Os { code: 2, kind: NotFound }` when the fixture is started with `--nf-tables`, but the same aarch64 fixture starts and reports `dns_names:true` with its default firewall. `/usr/sbin/ip`, `/usr/sbin/nft`, and `/dev/net/tun` are present. This matches the known dev-02 privileged nf_tables environment gap; I am using the default firewall there for DNS, while the privileged gate evidence remains the compute01 pass at 726ee694.
Author
Owner

J254 Linux proof update (head 726ee694f3b09fc9268b3fe24cb2ede6635c3b55):

  • Both nodes resolved the peer FQDNs and systemd-resolved had per-link routes for internal and mesh.j254.test. Overlay ping -4 and ping -6 failed in both directions; TCP/22 timed out. On compute01, incoming4_all and outgoing4_all were empty, and the TUN TX counter stayed at 0. I have not counted this as Linux connectivity proof.
  • The existing coordinator process returned an allow delta with no peer updates. A fresh full snapshot used version 3 against the existing node state; the node rejected it with InvalidData and exited. This left a DOWN persistent infraternal0 with no holder. I removed only that fixture-owned interface under the live mesh claim; I did not alter the nft table.
  • Capture immediately before cleanup:
    34: infraternal0: <NO-CARRIER,POINTOPOINT,MULTICAST,NOARP,UP> mtu 1380 qdisc fq state DOWN mode DEFAULT group default qlen 1024
        link/none  promiscuity 0 allmulti 0 minmtu 68 maxmtu 65535
        tun type tun pi off vnet_hdr on persist on user root group root addrgenmode random numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536 gso_ipv4_max_size 65536 gro_ipv4_max_size 65535
        alias infraternal.mesh.gotatun.v2:91689a2ef13ffe4f9e5818fb64d7232b40a83584b63716041422105cde91ae6f
    
  • Capture immediately after cleanup: Device "infraternal0" does not exist.

Next: restart the compute01 node with a snapshot newer than its retained version, reapply the valid full allow config to both nodes, then retry ping/SSH and denied-name byte comparison. Resolver cleanup and claim release remain pending; I will keep the claim under 60 minutes.

J254 Linux proof update (head `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`): - Both nodes resolved the peer FQDNs and systemd-resolved had per-link routes for `internal` and `mesh.j254.test`. Overlay `ping -4` and `ping -6` failed in both directions; TCP/22 timed out. On compute01, `incoming4_all` and `outgoing4_all` were empty, and the TUN TX counter stayed at 0. I have not counted this as Linux connectivity proof. - The existing coordinator process returned an allow delta with no peer updates. A fresh full snapshot used version 3 against the existing node state; the node rejected it with `InvalidData` and exited. This left a DOWN persistent `infraternal0` with no holder. I removed only that fixture-owned interface under the live mesh claim; I did not alter the nft table. - Capture immediately before cleanup: ``` 34: infraternal0: <NO-CARRIER,POINTOPOINT,MULTICAST,NOARP,UP> mtu 1380 qdisc fq state DOWN mode DEFAULT group default qlen 1024 link/none promiscuity 0 allmulti 0 minmtu 68 maxmtu 65535 tun type tun pi off vnet_hdr on persist on user root group root addrgenmode random numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536 gso_ipv4_max_size 65536 gro_ipv4_max_size 65535 alias infraternal.mesh.gotatun.v2:91689a2ef13ffe4f9e5818fb64d7232b40a83584b63716041422105cde91ae6f ``` - Capture immediately after cleanup: `Device "infraternal0" does not exist.` Next: restart the compute01 node with a snapshot newer than its retained version, reapply the valid full allow config to both nodes, then retry ping/SSH and denied-name byte comparison. Resolver cleanup and claim release remain pending; I will keep the claim under 60 minutes.
Author
Owner

J254 Linux claim closeout (head unchanged at 726ee694f3b09fc9268b3fe24cb2ede6635c3b55):

  • I released ~/mesh-vm.claim after shutting down the compute01 and dev-02 fixtures. Both node sessions returned {"off":true} on clean shutdown; both hosts now report Device "infraternal0" does not exist and no resolvectl link configuration remains.
  • Compute01's table inet infraternal_mesh with ownership comment infraternal.mesh.v1 remains after shutdown. I left it untouched as instructed.
  • Linux connectivity proof remains incomplete. Initial FQDN lookups resolved, but IPv4/IPv6 ping had 0/2 replies in both directions and TCP/22 timed out. The node firewall sets were empty. A fresh full allow snapshot at v3 was stale against the retained v4 config; after restart, v10 applied on compute01 but dev-02 returned Unsupported because the fresh coordinator had generated a different mesh IPv6 prefix than dev-02's bound resolver. The denied-name versus nonexistent byte comparison was not run.
  • This turn made no repository changes and ran no new gates. The prior quick/fast and privileged receipts remain at this head. Mac resolver work and prior DNS performance numbers remain as recorded earlier.

Next Linux attempt needs to reuse a single stable mesh address prefix across coordinator and both node snapshots, then prove ping/SSH and byte-identical denied-name NXDOMAIN before removing the resolver and closing the work item.

J254 Linux claim closeout (head unchanged at `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`): - I released `~/mesh-vm.claim` after shutting down the compute01 and dev-02 fixtures. Both node sessions returned `{"off":true}` on clean shutdown; both hosts now report `Device "infraternal0" does not exist` and no `resolvectl` link configuration remains. - Compute01's `table inet infraternal_mesh` with ownership comment `infraternal.mesh.v1` remains after shutdown. I left it untouched as instructed. - Linux connectivity proof remains incomplete. Initial FQDN lookups resolved, but IPv4/IPv6 ping had 0/2 replies in both directions and TCP/22 timed out. The node firewall sets were empty. A fresh full allow snapshot at v3 was stale against the retained v4 config; after restart, v10 applied on compute01 but dev-02 returned `Unsupported` because the fresh coordinator had generated a different mesh IPv6 prefix than dev-02's bound resolver. The denied-name versus nonexistent byte comparison was not run. - This turn made no repository changes and ran no new gates. The prior quick/fast and privileged receipts remain at this head. Mac resolver work and prior DNS performance numbers remain as recorded earlier. Next Linux attempt needs to reuse a single stable mesh address prefix across coordinator and both node snapshots, then prove ping/SSH and byte-identical denied-name NXDOMAIN before removing the resolver and closing the work item.
Author
Owner

J254 claim status: after releasing my previous claim, the claim file changed to J288 2026-11-07T13:37:23Z. My attempted reacquisition used shell noclobber and did not overwrite it. I have started no VM work while J288 owns the pair.

The next proof attempt will start both nodes from clean down state, then build/run the coordinator fixture with DNS enabled and create one snapshot from those two fresh identities. This should avoid the stale-version and cross-prefix failures from the previous fixture sequence. Waiting for J288 to release before proceeding.

J254 claim status: after releasing my previous claim, the claim file changed to `J288 2026-11-07T13:37:23Z`. My attempted reacquisition used shell noclobber and did not overwrite it. I have started no VM work while J288 owns the pair. The next proof attempt will start both nodes from clean `down` state, then build/run the coordinator fixture with DNS enabled and create one snapshot from those two fresh identities. This should avoid the stale-version and cross-prefix failures from the previous fixture sequence. Waiting for J288 to release before proceeding.
Author
Owner

J254 claim poll at 2026-11-07T13:48:52Z: J288 released; the shared ~/mesh-vm.claim now reads J287 2026-11-07T13:45:23Z. I have not used the pair. I will wait for J287 to release. The old J254 target has been deleted; compute01 has 62 GiB free at this check.

J254 claim poll at `2026-11-07T13:48:52Z`: J288 released; the shared `~/mesh-vm.claim` now reads `J287 2026-11-07T13:45:23Z`. I have not used the pair. I will wait for J287 to release. The old J254 target has been deleted; compute01 has 62 GiB free at this check.
Author
Owner

J254 reacquired the free mesh claim at 2026-11-07T13:52:04Z. The previous kernel_node binaries were built at 65f44579312b5a34d330135b79920440bb8cb556, not this branch head. I am rebuilding the x86_64/aarch64 node fixtures and DNS-enabled coordinator from 726ee694f3b09fc9268b3fe24cb2ede6635c3b55 on compute01 through build-slot, then I will start both nodes from clean down state and use one fresh coordinator snapshot. Compute01 had 60 GiB free at admission; the old target remains deleted.

J254 reacquired the free mesh claim at `2026-11-07T13:52:04Z`. The previous `kernel_node` binaries were built at `65f44579312b5a34d330135b79920440bb8cb556`, not this branch head. I am rebuilding the x86_64/aarch64 node fixtures and DNS-enabled coordinator from `726ee694f3b09fc9268b3fe24cb2ede6635c3b55` on compute01 through `build-slot`, then I will start both nodes from clean `down` state and use one fresh coordinator snapshot. Compute01 had 60 GiB free at admission; the old target remains deleted.
Author
Owner

J254 claim update: the fixture build is waiting in the foreground at compute01 build-slot queue position 9 (all 5 slots busy); Cargo has not started. I released my claim J254 2026-11-07T13:52:04Z while the build waits, and the claim file is now free. No live mesh or resolver state was changed during this second hold. I will reacquire only after the current-head fixtures finish and the shared claim is free.

J254 claim update: the fixture build is waiting in the foreground at compute01 build-slot queue position 9 (`all 5 slots busy`); Cargo has not started. I released my claim `J254 2026-11-07T13:52:04Z` while the build waits, and the claim file is now free. No live mesh or resolver state was changed during this second hold. I will reacquire only after the current-head fixtures finish and the shared claim is free.
Author
Owner

Resumed Linux closeout. The compute01 worktree is clean at 726ee694f3; the latest shared claim file names J288 (2026-11-07T13:58:47Z), so I have not touched mesh state. I am waiting in the foreground and will acquire atomically once its claim is removed. The last Linux attempt left connectivity, denied-name byte comparison, and resolver cleanup proof incomplete; I will rerun that sequence from clean node state.

Resumed Linux closeout. The compute01 worktree is clean at 726ee694f3b09fc9268b3fe24cb2ede6635c3b55; the latest shared claim file names J288 (2026-11-07T13:58:47Z), so I have not touched mesh state. I am waiting in the foreground and will acquire atomically once its claim is removed. The last Linux attempt left connectivity, denied-name byte comparison, and resolver cleanup proof incomplete; I will rerun that sequence from clean node state.
Author
Owner

J288 released the shared mesh claim; I acquired it atomically at 2026-11-07T14:05:11Z. Beginning the Linux closeout now. I will first capture compute01 interface state as root, then inspect the J207 branch-server PID, and proceed with the two-node proof.

J288 released the shared mesh claim; I acquired it atomically at 2026-11-07T14:05:11Z. Beginning the Linux closeout now. I will first capture compute01 interface state as root, then inspect the J207 branch-server PID, and proceed with the two-node proof.
Author
Owner

Linux live-state evidence (claim J254, 2026-11-07T14:05:11Z): as root on compute01, before cleanup ip -d link show infraternal0 printed Device "infraternal0" does not exist.; after the check it printed the same. No delete was needed because the interface was already absent. PID 3938718 and TCP listener :45680 were absent at start. I left the unrelated j93fixture PID 998 untouched.

Linux live-state evidence (claim J254, 2026-11-07T14:05:11Z): as root on compute01, before cleanup `ip -d link show infraternal0` printed `Device "infraternal0" does not exist.`; after the check it printed the same. No delete was needed because the interface was already absent. PID 3938718 and TCP listener :45680 were absent at start. I left the unrelated j93fixture PID 998 untouched.
Author
Owner

The claim is released now that the initial live-state check is complete. Compute01 and dev-02 both currently have no infraternal0; root-before/root-after on compute01 were identical absent-device output. PID 3938718 and :45680 were absent. The J254 target is absent, so current-head fixture binaries need preparation before I reacquire; I will keep the pair free during that build/queue and take the claim again only for the actual node run.

The claim is released now that the initial live-state check is complete. Compute01 and dev-02 both currently have no `infraternal0`; root-before/root-after on compute01 were identical absent-device output. PID 3938718 and :45680 were absent. The J254 target is absent, so current-head fixture binaries need preparation before I reacquire; I will keep the pair free during that build/queue and take the claim again only for the actual node run.
Author
Owner

Preparation finding: the retained kernel_node fixtures are from 65f44579, while this branch head is 726ee694. The compute01 target is absent, and the prepared current-head build script compiles x86_64 and aarch64 kernel_node plus the coordinator in one batch. The nix dev-shell preflight succeeded (cargo 1.99.0). Compute01 has 75 GiB free but all five slots are busy with eight queued; dev-02 has 43 GiB free against its 40 GiB floor, so I will keep the full fixture build on compute01 and leave the mesh claim free while it waits/runs.

Preparation finding: the retained kernel_node fixtures are from 65f44579, while this branch head is 726ee694. The compute01 target is absent, and the prepared current-head build script compiles x86_64 and aarch64 kernel_node plus the coordinator in one batch. The nix dev-shell preflight succeeded (`cargo 1.99.0`). Compute01 has 75 GiB free but all five slots are busy with eight queued; dev-02 has 43 GiB free against its 40 GiB floor, so I will keep the full fixture build on compute01 and leave the mesh claim free while it waits/runs.
Author
Owner

Queue/disk update: the foreground current-head fixture ticket is PID 2438363. build-slot rechecks disk before admission; compute01 has fallen to 13 GiB free (below its 25 GiB floor), so it is safely waiting and Cargo has not started. ~/mesh-vm.claim now names J288 again. I hold no mesh claim, and will leave the build ticket queued until disk admission is safe.

Queue/disk update: the foreground current-head fixture ticket is PID 2438363. `build-slot` rechecks disk before admission; compute01 has fallen to 13 GiB free (below its 25 GiB floor), so it is safely waiting and Cargo has not started. `~/mesh-vm.claim` now names J288 again. I hold no mesh claim, and will leave the build ticket queued until disk admission is safe.
Author
Owner

Foreground fixture-build update at 2026-11-07T14:40Z: build-slot PID 2438363 remains at queue position 5 after about 30 minutes; Cargo has not started. Compute01 has 149 GiB free, and the ticket log is /home/claude/infraternal-ops/j254-evidence/build-fixtures-726-current.log. The mesh claim currently names J288, so I hold no claim and have not started any node or resolver state.

Foreground fixture-build update at 2026-11-07T14:40Z: build-slot PID 2438363 remains at queue position 5 after about 30 minutes; Cargo has not started. Compute01 has 149 GiB free, and the ticket log is `/home/claude/infraternal-ops/j254-evidence/build-fixtures-726-current.log`. The mesh claim currently names J288, so I hold no claim and have not started any node or resolver state.
Author
Owner

Correction to my queue updates: a prior J254 foreground ticket already existed. It is now active in compute01 slot 4 as PID 1965948, running ~/infraternal-ops/j254-evidence/build-fixtures-726.sh. My later duplicate ticket PID 2438363 was terminated while still queued (exit 143); its log confirms Cargo never started. I will follow the original build and leave its target untouched. The shared claim remains J288.

Correction to my queue updates: a prior J254 foreground ticket already existed. It is now active in compute01 slot 4 as PID 1965948, running `~/infraternal-ops/j254-evidence/build-fixtures-726.sh`. My later duplicate ticket PID 2438363 was terminated while still queued (exit 143); its log confirms Cargo never started. I will follow the original build and leave its target untouched. The shared claim remains J288.
Author
Owner

Build status: the pre-existing J254 script is active as PID 1965948 with a cargo child (PID 3570197) compiling the current-head example; the J254 target is 1.2 GiB and compute01 has 142 GiB free. The separate duplicate request has exited 143 before Cargo. The active script is making progress; I will not touch its target or submit another build.

Build status: the pre-existing J254 script is active as PID 1965948 with a `cargo` child (PID 3570197) compiling the current-head example; the J254 target is 1.2 GiB and compute01 has 142 GiB free. The separate duplicate request has exited 143 before Cargo. The active script is making progress; I will not touch its target or submit another build.
Author
Owner

Current-head fixture build artifacts at 726ee694f3 are present and verified as stripped static executables: x86_64 kernel_node 6,288,528 bytes (SHA-256 be4409e72e1a46e62c6a191d9e74c076a6f0296e7fae9b3606927be3afce4305), aarch64 kernel_node 5,621,584 bytes (SHA-256 92d16c08efa4c42b267ed7c828a60ede1839bc0e9ee02babcaf62955929791bc), coordinator_fixture 3,881,632 bytes (SHA-256 bf967226a32a0184a585a17743ec3a7ba42f2d173c314c00253ea44e135dab48). The target was 2.0 GiB and has been deleted after the build process exited; compute01 has 193 GiB free. Worktree remains clean. The original build session output was not retained, so these file/hash checks are the available artifact evidence.

Current-head fixture build artifacts at 726ee694f3b09fc9268b3fe24cb2ede6635c3b55 are present and verified as stripped static executables: x86_64 kernel_node 6,288,528 bytes (SHA-256 be4409e72e1a46e62c6a191d9e74c076a6f0296e7fae9b3606927be3afce4305), aarch64 kernel_node 5,621,584 bytes (SHA-256 92d16c08efa4c42b267ed7c828a60ede1839bc0e9ee02babcaf62955929791bc), coordinator_fixture 3,881,632 bytes (SHA-256 bf967226a32a0184a585a17743ec3a7ba42f2d173c314c00253ea44e135dab48). The target was 2.0 GiB and has been deleted after the build process exited; compute01 has 193 GiB free. Worktree remains clean. The original build session output was not retained, so these file/hash checks are the available artifact evidence.
Author
Owner

SSH preparation: both meshtest accounts have authorized_keys but no SSH client key/config in their homes. For the one-way allowed SSH proof I will generate a temporary test key, append only its public key to the target meshtest account, and remove that exact key afterward; I will record and compare the target authorized_keys hash to confirm cleanup. No key contents will be logged.

SSH preparation: both `meshtest` accounts have `authorized_keys` but no SSH client key/config in their homes. For the one-way allowed SSH proof I will generate a temporary test key, append only its public key to the target `meshtest` account, and remove that exact key afterward; I will record and compare the target authorized_keys hash to confirm cleanup. No key contents will be logged.
Author
Owner

The aarch64 fixture was copied to dev-02 /tmp/j254-kernel-node-aarch64-musl-726; its SHA-256 matches the compute01 artifact. No build ran there. Dev-02 currently has 37 GiB free, so it remains runtime-only. Compute01 target is removed. At the last poll (15:26Z), the claim still named J288; I am continuing the foreground wait and will acquire atomically when it clears.

The aarch64 fixture was copied to dev-02 `/tmp/j254-kernel-node-aarch64-musl-726`; its SHA-256 matches the compute01 artifact. No build ran there. Dev-02 currently has 37 GiB free, so it remains runtime-only. Compute01 target is removed. At the last poll (15:26Z), the claim still named J288; I am continuing the foreground wait and will acquire atomically when it clears.
Author
Owner

J254 Linux proof update (claim J254 2026-11-07T15:35:27Z; branch head 726ee694f3b09fc9268b3fe24cb2ede6635c3b55):

  • Both current-head fixture nodes are live, using one fresh coordinator snapshot (version 3) and addresses 10.254.0.1 / 10.254.0.2, fdcb:eea6:995f::1 / ::2. Compute01's infraternal0 is UP and held by the live kernel node (PID 102517); it is not the orphan state and I have not deleted it.
  • Compute01 resolvectl status infraternal0 shows DNS server 10.254.0.1:1053, domains mesh.j254.test internal, and Default Route: no. Both infraternal-dev-02-std.internal and infraternal-dev-02-std.mesh.j254.test resolve to 10.254.0.2 and fdcb:eea6:995f::2. resolvectl query infraternal-dev-02-std fails with no requested RR; getent hosts infraternal-dev-02-std returns no result. ping -c 2 infraternal-dev-02-std.internal received 2/2 replies (148–161 ms).
  • From dev-02, denied compute01 names returned NXDOMAIN; comparison to a same-length nonexistent label had byte-identical response sections after the differing question for both suffixes. Negative-section SHA-256: c51f2d15d69f10a6db5acac0973800123eeddb2fbe8849b2d9a27e90c659eb9a; samples were 103–252 µs.
  • Mesh SSH host key obtained by ssh-keyscan over 10.254.0.2 matches the recorded out-of-band fingerprint SHA256:/ls76Gxd9Of1kD9GyZe95tsEHNRiXsxUijbnf9j2GuM. SSH authentication is not yet set up; existing meshtest and claude identities are rejected. I requested the linked dev-02 environment to install one temporary public key, test SSH with strict host-key checking, then remove it and restore the original authorized_keys hash.

SSH resolution and resolver cleanup remain pending. I will stop both fixtures and release the claim as soon as those checks are complete.

J254 Linux proof update (claim `J254 2026-11-07T15:35:27Z`; branch head `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`): - Both current-head fixture nodes are live, using one fresh coordinator snapshot (version 3) and addresses `10.254.0.1` / `10.254.0.2`, `fdcb:eea6:995f::1` / `::2`. Compute01's `infraternal0` is UP and held by the live kernel node (PID 102517); it is not the orphan state and I have not deleted it. - Compute01 `resolvectl status infraternal0` shows DNS server `10.254.0.1:1053`, domains `mesh.j254.test internal`, and `Default Route: no`. Both `infraternal-dev-02-std.internal` and `infraternal-dev-02-std.mesh.j254.test` resolve to `10.254.0.2` and `fdcb:eea6:995f::2`. `resolvectl query infraternal-dev-02-std` fails with no requested RR; `getent hosts infraternal-dev-02-std` returns no result. `ping -c 2 infraternal-dev-02-std.internal` received 2/2 replies (148–161 ms). - From dev-02, denied compute01 names returned NXDOMAIN; comparison to a same-length nonexistent label had byte-identical response sections after the differing question for both suffixes. Negative-section SHA-256: `c51f2d15d69f10a6db5acac0973800123eeddb2fbe8849b2d9a27e90c659eb9a`; samples were 103–252 µs. - Mesh SSH host key obtained by `ssh-keyscan` over `10.254.0.2` matches the recorded out-of-band fingerprint `SHA256:/ls76Gxd9Of1kD9GyZe95tsEHNRiXsxUijbnf9j2GuM`. SSH authentication is not yet set up; existing `meshtest` and `claude` identities are rejected. I requested the linked dev-02 environment to install one temporary public key, test SSH with strict host-key checking, then remove it and restore the original authorized_keys hash. SSH resolution and resolver cleanup remain pending. I will stop both fixtures and release the claim as soon as those checks are complete.
Author
Owner

J254 Linux closeout (branch head unchanged at 726ee694f3b09fc9268b3fe24cb2ede6635c3b55). I released ~/mesh-vm.claim at 2026-11-07T16:28:57Z, after holding J254 2026-11-07T15:35:27Z for 53m30s.

  • Both persistent fixture sessions accepted quit and returned {"off":true}. Compute01's active tun before stop was held by the live node (PID 102517), so it was not an orphan then. Root fuser /dev/net/tun after stop showed only tailscaled. resolvectl status infraternal0 now reports no such device.
  • Requested root ip -d link capture before stop:
    44: infraternal0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1380 qdisc fq state UNKNOWN mode DEFAULT group default qlen 1024
        link/none  promiscuity 0 allmulti 0 minmtu 68 maxmtu 65535
        tun type tun pi off vnet_hdr on persist on user root group root addrgenmode random numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536 gso_ipv4_max_size 65536 gro_ipv4_max_size 65536
        alias infraternal.mesh.gotatun.v2:251667a4b799f75fcf5815a1bf930a14b56397c8996670217702b2aa10d4831b
    
  • Root ip -d link after stop printed exactly Device "infraternal0" does not exist. No manual ip link delete was needed; the live node removed its own link on clean shutdown.
  • .internal and mesh.j254.test FQDN lookups returned the peer addresses. Explicit .internal IPv4 and IPv6 ping trials each received 2/2 replies (149–201 ms and 150–154 ms). One earlier default ping trial got 0/2, then direct and FQDN retries succeeded. Bare infraternal-dev-02-std lookup is inconsistent: ping and default getent fail; resolvectl query returns the VPN address 10.69.69.117 on wt0, while forced nss-resolve returns the mesh address via mesh.j254.test. Plain ssh <hostname> is therefore not proven and remains an acceptance gap.
  • The denied-name comparison from dev-02 returned NXDOMAIN for both suffixes; bytes after the differing question matched the same-length nonexistent label, including the same SOA. Negative-section SHA-256: c51f2d15d69f10a6db5acac0973800123eeddb2fbe8849b2d9a27e90c659eb9a; samples were 103–252 µs.
  • ssh-keyscan over 10.254.0.2 matched the out-of-band fingerprint SHA256:/ls76Gxd9Of1kD9GyZe95tsEHNRiXsxUijbnf9j2GuM, but SSH login was not completed: existing meshtest and claude keys were rejected, and ssh cua is not resolvable from this environment. The temporary key was never installed; no authorized_keys write was made. The pre-recorded target hash is 7c5bf5e472b11a849337b278a12a7fc424c9a57fe4efda0b8de55f58f98c24f2.
  • The dev-02 fixture session also returned {"off":true}, but I could not independently inspect its post-stop link/resolver or remove /tmp/j254-kernel-node-aarch64-musl-726 without an authenticated dev-02 shell. The dev-02 environment-link request is pending. This cleanup and the SSH proof remain outstanding.
  • Existing load evidence remains: DNS query p50 75 µs, p99 90 µs, and 135,168 bytes per 1,000 names. Existing current-head quick/fast and privileged receipts are already on this issue. No repository changes were made in this live run.
J254 Linux closeout (branch head unchanged at `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`). I released `~/mesh-vm.claim` at `2026-11-07T16:28:57Z`, after holding `J254 2026-11-07T15:35:27Z` for 53m30s. - Both persistent fixture sessions accepted `quit` and returned `{"off":true}`. Compute01's active tun before stop was held by the live node (PID 102517), so it was not an orphan then. Root `fuser /dev/net/tun` after stop showed only tailscaled. `resolvectl status infraternal0` now reports no such device. - Requested root `ip -d link` capture before stop: ``` 44: infraternal0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1380 qdisc fq state UNKNOWN mode DEFAULT group default qlen 1024 link/none promiscuity 0 allmulti 0 minmtu 68 maxmtu 65535 tun type tun pi off vnet_hdr on persist on user root group root addrgenmode random numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536 gso_ipv4_max_size 65536 gro_ipv4_max_size 65536 alias infraternal.mesh.gotatun.v2:251667a4b799f75fcf5815a1bf930a14b56397c8996670217702b2aa10d4831b ``` - Root `ip -d link` after stop printed exactly `Device "infraternal0" does not exist.` No manual `ip link delete` was needed; the live node removed its own link on clean shutdown. - `.internal` and `mesh.j254.test` FQDN lookups returned the peer addresses. Explicit `.internal` IPv4 and IPv6 ping trials each received 2/2 replies (149–201 ms and 150–154 ms). One earlier default ping trial got 0/2, then direct and FQDN retries succeeded. Bare `infraternal-dev-02-std` lookup is inconsistent: `ping` and default `getent` fail; `resolvectl query` returns the VPN address `10.69.69.117` on `wt0`, while forced `nss-resolve` returns the mesh address via `mesh.j254.test`. Plain `ssh <hostname>` is therefore not proven and remains an acceptance gap. - The denied-name comparison from dev-02 returned NXDOMAIN for both suffixes; bytes after the differing question matched the same-length nonexistent label, including the same SOA. Negative-section SHA-256: `c51f2d15d69f10a6db5acac0973800123eeddb2fbe8849b2d9a27e90c659eb9a`; samples were 103–252 µs. - `ssh-keyscan` over `10.254.0.2` matched the out-of-band fingerprint `SHA256:/ls76Gxd9Of1kD9GyZe95tsEHNRiXsxUijbnf9j2GuM`, but SSH login was not completed: existing `meshtest` and `claude` keys were rejected, and `ssh cua` is not resolvable from this environment. The temporary key was never installed; no `authorized_keys` write was made. The pre-recorded target hash is `7c5bf5e472b11a849337b278a12a7fc424c9a57fe4efda0b8de55f58f98c24f2`. - The dev-02 fixture session also returned `{"off":true}`, but I could not independently inspect its post-stop link/resolver or remove `/tmp/j254-kernel-node-aarch64-musl-726` without an authenticated dev-02 shell. The dev-02 environment-link request is pending. This cleanup and the SSH proof remain outstanding. - Existing load evidence remains: DNS query p50 75 µs, p99 90 µs, and 135,168 bytes per 1,000 names. Existing current-head quick/fast and privileged receipts are already on this issue. No repository changes were made in this live run.
Author
Owner

J254 resumed the Linux live proof and atomically reacquired ~/mesh-vm.claim at 2026-11-07T16:37:55Z. D327 covers privacy for denied lookups; D339 names use the reported hostname. #266 explicitly requires ping <name>.internal and ssh <name>, so I am treating bare SSH names as part of the acceptance and will test the system resolver path before changing code.

J254 resumed the Linux live proof and atomically reacquired `~/mesh-vm.claim` at `2026-11-07T16:37:55Z`. D327 covers privacy for denied lookups; D339 names use the reported hostname. #266 explicitly requires `ping <name>.internal` and `ssh <name>`, so I am treating bare SSH names as part of the acceptance and will test the system resolver path before changing code.
Author
Owner

J254 Linux live-proof closeout on branch mesh-dns-j254, head 726ee694f3b09fc9268b3fe24cb2ede6635c3b55 (unchanged). The shared claim J254 2026-11-07T16:37:55Z was released at 2026-11-07T17:23:25Z after stopping and cleaning both nodes.

Live DNS, reachability, and SSH

I restarted both current-head kernel fixtures and made a fresh coordinator snapshot from those exact node identities. One-way policy allowed compute01 to dev-02; dev-02 had no compute01 name in its snapshot. The nodes received 10.254.0.1 / 10.254.0.2 and fd5e:2e41:ef47::1 / ::2.

Both infraternal0 links had a per-link DNS server on port 1053 and domains mesh.j254.test internal. Compute01 reported Default Route: no. On compute01, both infraternal-dev-02-std.internal and infraternal-dev-02-std.mesh.j254.test resolved to 10.254.0.2 / fd5e:2e41:ef47::2. IPv4 ping to .internal received 2/2 replies (150.770–164.415 ms); IPv6 ping received 2/2 replies (158.593–162.573 ms).

The temporary key marker j254-temp-20261107T1716Z was installed only on dev-02's meshtest account. From compute01's meshtest account, strict-host-key SSH to meshtest@infraternal-dev-02-std.internal succeeded. The target reported source fd5e:2e41:ef47::1, confirming the connection used the mesh. The scanned host-key fingerprint matched the previously recorded out-of-band fingerprint SHA256:/ls76Gxd9Of1kD9GyZe95tsEHNRiXsxUijbnf9j2GuM.

From dev-02, both compute01 denied names returned NXDOMAIN, and direct reverse TCP/22 was blocked. I sent 50 raw UDP A queries for each denied name and a same-length nonexistent label to dev-02's listener. For both suffixes, the response header and every byte after the differing question matched, including the one SOA record:

  • .internal: denied p50/p99 74.8/263.8 µs; nonexistent p50/p99 79.7/132.8 µs.
  • mesh.j254.test: denied p50/p99 52.9/105.4 µs; nonexistent p50/p99 45.9/134.8 µs.

The small live samples overlap; the existing DNS regression exercises both cases through the same negative-response path.

Bare-name contract finding

D165 defines the DNS name as <host>.internal by default. D197 explicitly promises plain ssh host.internal; D327 concerns denied-name visibility; D339 defines the hostname label. The #266 plan also scopes names to <host>.internal plus an optional suffix. No numbered decision promises a bare single-label alias.

On compute01, getent hosts infraternal-dev-02-std returned the mesh AAAA through the custom suffix, but plain ssh infraternal-dev-02-std failed with No address associated with hostname. On dev-02, hosts: files dns uses the stub resolver and a bare compute01 name resolves to the existing VPN hostname/address. Changing host-wide NSS/search behavior would exceed the current contract, so I made no code or NSS change. FQDN SSH is proven above; bare SSH needs an owner decision if it should become a product guarantee.

Cleanup

The root ip -d link captures immediately before clean stop were:

compute01:
55: infraternal0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1380 qdisc fq state UNKNOWN mode DEFAULT group default qlen 1024
    link/none  promiscuity 0 allmulti 0 minmtu 68 maxmtu 65535
    tun type tun pi off vnet_hdr on persist on user root group root addrgenmode random numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65535 gro_max_size 65536 gso_ipv4_max_size 65536 gro_ipv4_max_size 65536
    alias infraternal.mesh.gotatun.v2:31e3d19db6a49dd68d80b5237ce47ca83d1e4217b5e465a6bf38cf92f3321406

dev-02:
37: infraternal0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1380 qdisc fq_codel state UNKNOWN mode DEFAULT group default qlen 1024
    link/none  promiscuity 0 allmulti 0 minmtu 68 maxmtu 65535
    tun type tun pi off vnet_hdr on persist on user root group root addrgenmode random numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 gro_max_size 65536
    alias infraternal.mesh.gotatun.v2:58b59d26df8242b8d95c79bfce97475d5e806ef3c29eab9fdd43c27c962eb61a

Both nodes returned {"off":true}. After stop, root ip -d link show infraternal0 printed Device "infraternal0" does not exist. on each host; resolvectl status infraternal0 reported no such device. Compute01's only remaining /dev/net/tun holder was tailscaled (PID 847); dev-02 had no holder. Dev-02's /etc/resolv.conf search list returned to vpn.kayg.org. The exact temporary authorized_keys line was removed; the file is back to its original SHA-256 7c5bf5e472b11a849337b278a12a7fc424c9a57fe4efda0b8de55f58f98c24f2, with original owner and mode. I removed /tmp/j254-kernel-node-aarch64-musl-726 from dev-02 and all temporary SSH key files from compute01.

Gates and size

The current-head verify quick, verify fast, and privileged receipts were already recorded earlier on this issue; this live-only continuation made no source changes. verify quick: server 556 passed / 0 failed / 26 ignored; bin-only 216 passed / 0 failed / 15 ignored. The foreground compute01 verify fast Rust table was:

| Rust gate                 | Result       |
+---------------------------+--------------+
| Nix Cargo vendoring       | PASS         |
| fmt                       | PASS         |
| migration names           | PASS         |
| migration tooling tests   | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out   | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| real-binary fixture paths | PASS         |
| Traefik component manifest | PASS         |
| release component publication | PASS     |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| doc coverage ratchet      | PASS         |
| shape lints off (D351)    | PASS         |
| doc comments on diff      | PASS         |
| fixed test waits          | PASS         |
| privileged integration    | PASS         |
| probe release exclusion   | PASS         |
| nightly fuzz reporter     | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+

All 96 privileged integration cases passed; TypeScript protocol tests passed 50/50 with 77,253 expectations. Static sizes were x86_64 musl 28,058,672 bytes (+5,640,416 vs baseline) and aarch64 musl 25,227,176 bytes (+4,773,896). Existing load benchmark: 80,000 queries / 8 clients, p50 75 µs, p99 90 µs, and +135,168 bytes RSS per 1,000 names.

J254 Linux live-proof closeout on branch `mesh-dns-j254`, head `726ee694f3b09fc9268b3fe24cb2ede6635c3b55` (unchanged). The shared claim `J254 2026-11-07T16:37:55Z` was released at `2026-11-07T17:23:25Z` after stopping and cleaning both nodes. ## Live DNS, reachability, and SSH I restarted both current-head kernel fixtures and made a fresh coordinator snapshot from those exact node identities. One-way policy allowed compute01 to dev-02; dev-02 had no compute01 name in its snapshot. The nodes received `10.254.0.1` / `10.254.0.2` and `fd5e:2e41:ef47::1` / `::2`. Both `infraternal0` links had a per-link DNS server on port 1053 and domains `mesh.j254.test internal`. Compute01 reported `Default Route: no`. On compute01, both `infraternal-dev-02-std.internal` and `infraternal-dev-02-std.mesh.j254.test` resolved to `10.254.0.2` / `fd5e:2e41:ef47::2`. IPv4 ping to `.internal` received 2/2 replies (150.770–164.415 ms); IPv6 ping received 2/2 replies (158.593–162.573 ms). The temporary key marker `j254-temp-20261107T1716Z` was installed only on dev-02's `meshtest` account. From compute01's `meshtest` account, strict-host-key SSH to `meshtest@infraternal-dev-02-std.internal` succeeded. The target reported source `fd5e:2e41:ef47::1`, confirming the connection used the mesh. The scanned host-key fingerprint matched the previously recorded out-of-band fingerprint `SHA256:/ls76Gxd9Of1kD9GyZe95tsEHNRiXsxUijbnf9j2GuM`. From dev-02, both compute01 denied names returned NXDOMAIN, and direct reverse TCP/22 was blocked. I sent 50 raw UDP A queries for each denied name and a same-length nonexistent label to dev-02's listener. For both suffixes, the response header and every byte after the differing question matched, including the one SOA record: - `.internal`: denied p50/p99 74.8/263.8 µs; nonexistent p50/p99 79.7/132.8 µs. - `mesh.j254.test`: denied p50/p99 52.9/105.4 µs; nonexistent p50/p99 45.9/134.8 µs. The small live samples overlap; the existing DNS regression exercises both cases through the same negative-response path. ## Bare-name contract finding D165 defines the DNS name as `<host>.internal` by default. D197 explicitly promises plain `ssh host.internal`; D327 concerns denied-name visibility; D339 defines the hostname label. The #266 plan also scopes names to `<host>.internal` plus an optional suffix. No numbered decision promises a bare single-label alias. On compute01, `getent hosts infraternal-dev-02-std` returned the mesh AAAA through the custom suffix, but plain `ssh infraternal-dev-02-std` failed with `No address associated with hostname`. On dev-02, `hosts: files dns` uses the stub resolver and a bare compute01 name resolves to the existing VPN hostname/address. Changing host-wide NSS/search behavior would exceed the current contract, so I made no code or NSS change. FQDN SSH is proven above; bare SSH needs an owner decision if it should become a product guarantee. ## Cleanup The root `ip -d link` captures immediately before clean stop were: ```text compute01: 55: infraternal0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1380 qdisc fq state UNKNOWN mode DEFAULT group default qlen 1024 link/none promiscuity 0 allmulti 0 minmtu 68 maxmtu 65535 tun type tun pi off vnet_hdr on persist on user root group root addrgenmode random numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65535 gro_max_size 65536 gso_ipv4_max_size 65536 gro_ipv4_max_size 65536 alias infraternal.mesh.gotatun.v2:31e3d19db6a49dd68d80b5237ce47ca83d1e4217b5e465a6bf38cf92f3321406 dev-02: 37: infraternal0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1380 qdisc fq_codel state UNKNOWN mode DEFAULT group default qlen 1024 link/none promiscuity 0 allmulti 0 minmtu 68 maxmtu 65535 tun type tun pi off vnet_hdr on persist on user root group root addrgenmode random numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 gro_max_size 65536 alias infraternal.mesh.gotatun.v2:58b59d26df8242b8d95c79bfce97475d5e806ef3c29eab9fdd43c27c962eb61a ``` Both nodes returned `{"off":true}`. After stop, root `ip -d link show infraternal0` printed `Device "infraternal0" does not exist.` on each host; `resolvectl status infraternal0` reported no such device. Compute01's only remaining `/dev/net/tun` holder was tailscaled (PID 847); dev-02 had no holder. Dev-02's `/etc/resolv.conf` search list returned to `vpn.kayg.org`. The exact temporary authorized_keys line was removed; the file is back to its original SHA-256 `7c5bf5e472b11a849337b278a12a7fc424c9a57fe4efda0b8de55f58f98c24f2`, with original owner and mode. I removed `/tmp/j254-kernel-node-aarch64-musl-726` from dev-02 and all temporary SSH key files from compute01. ## Gates and size The current-head `verify quick`, `verify fast`, and privileged receipts were already recorded earlier on this issue; this live-only continuation made no source changes. `verify quick`: server 556 passed / 0 failed / 26 ignored; bin-only 216 passed / 0 failed / 15 ignored. The foreground compute01 `verify fast` Rust table was: ```text | Rust gate | Result | +---------------------------+--------------+ | Nix Cargo vendoring | PASS | | fmt | PASS | | migration names | PASS | | migration tooling tests | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | Traefik component manifest | PASS | | release component publication | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | doc coverage ratchet | PASS | | shape lints off (D351) | PASS | | doc comments on diff | PASS | | fixed test waits | PASS | | privileged integration | PASS | | probe release exclusion | PASS | | nightly fuzz reporter | PASS | | static builds | PASS | +---------------------------+--------------+ ``` All 96 privileged integration cases passed; TypeScript protocol tests passed 50/50 with 77,253 expectations. Static sizes were x86_64 musl 28,058,672 bytes (+5,640,416 vs baseline) and aarch64 musl 25,227,176 bytes (+4,773,896). Existing load benchmark: 80,000 queries / 8 clients, p50 75 µs, p99 90 µs, and +135,168 bytes RSS per 1,000 names.
Author
Owner

J354 independent correctness/security review started. Branch review-j354, reviewing mesh-dns-j254 at 726ee694f3b09fc9268b3fe24cb2ede6635c3b55; merge base against current origin/dev is 3644950b8e4b5dddeefba350f97a3892189ca193. Plan (~90 minutes): read all #266 records and D165/D197/D327/D339; audit policy privacy, parser bounds, host cleanup and compatibility; run DNS-focused tests and adversarial reproducers on dev-02 through the preflight-checked build-slot launcher; post APPROVE or REQUEST CHANGES with artifacts. No merge or deployment. Per common brief reviewer rule, no full quick/fast rerun.

J354 independent correctness/security review started. Branch `review-j354`, reviewing `mesh-dns-j254` at `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`; merge base against current origin/dev is `3644950b8e4b5dddeefba350f97a3892189ca193`. Plan (~90 minutes): read all #266 records and D165/D197/D327/D339; audit policy privacy, parser bounds, host cleanup and compatibility; run DNS-focused tests and adversarial reproducers on dev-02 through the preflight-checked build-slot launcher; post APPROVE or REQUEST CHANGES with artifacts. No merge or deployment. Per common brief reviewer rule, no full quick/fast rerun.
Author
Owner

J354 confirmed findings at reviewed head 726ee694f3b09fc9268b3fe24cb2ede6635c3b55 (focused dev-02 runs, no full-gate rerun):

  1. P2, upgrade: node_coordination::read_identity includes dns_names in both probes. A strict pre-DNS infraternal agent rejects both, then answers {} without negotiated rotation/compact flags. The real coordinator classifies rotation as None and compact access as false. Store::attach_named_with_probed_capability refuses an open rotation when support is unknown; otherwise this disables the mesh-wide rotation gate. Reproducer failed with left: None / right: Some(true). Artifact: dev-02 /home/claude/infraternal-ops/j354-evidence/review-repro.log. A compile-out control is queued.

  2. P2, host integration: networkmanager_route_commands supplies each search domain as a separate argv value. The actual nmcli 1.46 offline property parser returns Error: invalid <setting>.<property> ~internal (exit 2). The same domains in one comma-separated argument parse successfully (exit 0). Thus the advertised no-resolved fallback never installs even the single internal suffix. Artifact: dev-02 /home/claude/infraternal-ops/j354-evidence/nmcli-property-parser.log; no host connection or DNS setting was changed.

  3. P2, robustness gate: cargo check --locked --manifest-path crates/plugins/mesh/fuzz/Cargo.toml --bin dns fails with E0433 (nix::unistd::Uid, missing user feature) and E0063 (fixture missing Config.exposure). The first error is introduced by the new port-53 path; the fixture drift predates it. Artifact: dev-02 /home/claude/infraternal-ops/j354-evidence/fuzz-target-check.log.

  4. P2, DNS interoperability: the promoted resolver binds UDP only on 1053/53. A TCP connection to the live test resolver receives ConnectionRefused, so TCP DNS clients cannot resolve even allowed A names. This was also a spike limitation; promotion leaves it in the production resolver. Artifact: dev-02 /home/claude/infraternal-ops/j354-evidence/dns-matrix-serial.log.

The shipped focused targets passed: access_updates 5/5 (3 privileged ignored), configuration 25/25, DNS 13/13 (1 privileged ignored). The independent UDP privacy matrix and 100,000-input parser sweep passed. Final report and committed regressions follow after the remaining focused checks and cleanup.

J354 confirmed findings at reviewed head `726ee694f3b09fc9268b3fe24cb2ede6635c3b55` (focused dev-02 runs, no full-gate rerun): 1. **P2, upgrade:** `node_coordination::read_identity` includes `dns_names` in both probes. A strict pre-DNS infraternal agent rejects both, then answers `{}` without negotiated rotation/compact flags. The real coordinator classifies rotation as `None` and compact access as false. `Store::attach_named_with_probed_capability` refuses an open rotation when support is unknown; otherwise this disables the mesh-wide rotation gate. Reproducer failed with `left: None / right: Some(true)`. Artifact: dev-02 `/home/claude/infraternal-ops/j354-evidence/review-repro.log`. A compile-out control is queued. 2. **P2, host integration:** `networkmanager_route_commands` supplies each search domain as a separate argv value. The actual nmcli 1.46 offline property parser returns `Error: invalid <setting>.<property> ~internal` (exit 2). The same domains in one comma-separated argument parse successfully (exit 0). Thus the advertised no-resolved fallback never installs even the single internal suffix. Artifact: dev-02 `/home/claude/infraternal-ops/j354-evidence/nmcli-property-parser.log`; no host connection or DNS setting was changed. 3. **P2, robustness gate:** `cargo check --locked --manifest-path crates/plugins/mesh/fuzz/Cargo.toml --bin dns` fails with E0433 (`nix::unistd::Uid`, missing `user` feature) and E0063 (fixture missing `Config.exposure`). The first error is introduced by the new port-53 path; the fixture drift predates it. Artifact: dev-02 `/home/claude/infraternal-ops/j354-evidence/fuzz-target-check.log`. 4. **P2, DNS interoperability:** the promoted resolver binds UDP only on 1053/53. A TCP connection to the live test resolver receives `ConnectionRefused`, so TCP DNS clients cannot resolve even allowed A names. This was also a spike limitation; promotion leaves it in the production resolver. Artifact: dev-02 `/home/claude/infraternal-ops/j354-evidence/dns-matrix-serial.log`. The shipped focused targets passed: access_updates 5/5 (3 privileged ignored), configuration 25/25, DNS 13/13 (1 privileged ignored). The independent UDP privacy matrix and 100,000-input parser sweep passed. Final report and committed regressions follow after the remaining focused checks and cleanup.
Author
Owner

J354 independent correctness/security review: REQUEST CHANGES

reviewed head: 726ee694f3b09fc9268b3fe24cb2ede6635c3b55. review-only branch: review-j354 at 7f35610adc4d7159d86fa5cee7d1f86bf2c0f90a. no merge, deploy, or push to dev.

Reviewed mesh-dns-j254 at 726ee694f3b09fc9268b3fe24cb2ede6635c3b55
against origin/dev, merge base 3644950b8e4b5dddeefba350f97a3892189ca193.
Verdict: REQUEST CHANGES. Four P2 findings; no P1 established.
The review branch contains deliberately failing regressions, not production fixes.

Findings

  1. P2: enabling DNS loses existing nodes' independent capabilities.
    crates/plugins/mesh/src/node_coordination.rs:41 puts dns_names in both
    negotiation probes. A strict pre-DNS infraternal agent rejects both. Its
    ordinary {} reply omits unrequested capability fields, so the coordinator
    records rotation support as unknown and compact access as false. If that
    node has an open rotation, store.rs:1483 refuses attachment with
    ROTATION_IN_PROGRESS; repeated negotiation cannot recover. Otherwise the
    false rotation capability closes the mesh-wide rotation gate. Preserve the
    pre-DNS probes when independently negotiating the DNS extension.
    review-repro.log reproduces this through the actual read_identity entry
    point. identity-without-dns.log passes the same test with DNS compiled out.

  2. P2: the NetworkManager split-DNS fallback cannot install its route.
    crates/plugins/mesh/src/dns_integration.rs:310 passes the search-domain list
    as separate arguments after ipv4.dns-search. The real nmcli 1.46 offline
    parser treats ~internal as a property and exits 2:
    Error: invalid <setting>.<property> '~internal'.
    Joining the same domains into one comma-separated value exits 0.
    nmcli-property-parser.log holds both runs; no host connection was modified.
    Supply one list-valued property argument and test the actual CLI grammar.

  3. P2: the production-promoted DNS service has no TCP listener.
    crates/plugins/mesh/src/dns.rs:399 binds UDP only, including the optional
    system port. A TCP connect to the running resolver on its configured port
    returns ConnectionRefused (errno 111). TCP clients and clients falling
    back to TCP cannot resolve mesh names. This was a spike limitation, now
    relevant to default-on DNS interoperability (RFC 7766). Add a bounded TCP
    path with the same source/privacy rules. dns-matrix-serial.log records the
    independent reproducer. The earlier parallel run in dns-focused.log had
    a review-harness port collision and is not evidence for this finding.

  4. P2: the DNS fuzz target does not compile.
    cargo check --locked -j3 --manifest-path crates/plugins/mesh/fuzz/Cargo.toml --bin dns exits 101. The new dns.rs:520 use of nix::unistd::Uid requires
    the user feature missing from the fuzz manifest (E0433). The fixture also
    lacks Config.exposure (E0063, preexisting drift). Repair both so the
    actual parser target can run. fuzz-target-check.log is the raw receipt.

Checks and limits

Focused tests run on dev-02 through build-slot, with its exact Nix cargo
launcher checked first, CARGO_INCREMENTAL=0, and one target directory.
Existing suites in dns-focused.log: access updates 5 passed, 3 privileged
ignored; configuration 25 passed; DNS 13 passed, 1 privileged ignored.
The final serial run in final-focused.log has two expected failing library
regressions and four passing DNS checks plus the expected TCP failure; cargo
exits 101. The six existing route tests and one legacy identity-shape test
pass separately, each cargo exit 0. The wrapper exits 0 after collecting all
statuses, which is not an overall green result. Its status labels accidentally
end with a literal n; the cargo summaries and statuses remain intact.

The custom UDP matrix compares 16 pairs across two nonlocal source addresses,
A/AAAA/ANY/PTR, mixed case and final-dot presentation. Denied and missing
answers have equal sizes and identical headers and records after excluding
the echoed question: NXDOMAIN, SOA TTL 5. A self-source positive control
answers. Nonlocal callers receive NXDOMAIN, not a silent drop. This proves
the response behavior using loopback source addresses; no live LAN neighbour
test or privacy timing benchmark was performed. Source inspection shows
nonlocal callers take a common negative path before any name-table lookup.
Denied peer names are omitted from the local table, so local denied and
nonexistent lookups have no separate denial-state branch.

The deterministic parser sweep completes 100,000 random packets, compression
loops, extreme counts, a 65,535-byte packet and a positive recovery query.
The socket/parser bound is 4,096 bytes with a 4,097-byte receive sentinel;
the worker does not spawn a task per packet. This cheap corpus does not
replace the broken fuzz target. Reverse zones are outside mesh authority.
Four concurrent clients each complete 1,000 UDP queries and the resolver
stops cleanly, recorded in final-focused.log without a speed assertion.

DNS name hiding does not provide complete peer-address confidentiality:
configuration still carries denied peers' keys and addresses in Config.peers.
That is preexisting architecture, not a newly discovered DNS disclosure.

Linux resolved commands target only the mesh link, set default-route no,
and do not replace global DNS. macOS writes require root, refuse unsafe
directories/targets, preserve unrelated resolver files and atomically rename
staged files from outside /etc/resolver. Cleanup is owned-file scoped.
Author clean-stop proofs are evidence of clean stop, not abrupt-stop proof:
Drop cannot run after SIGKILL and persistent Mac resolver files can remain
until restart or explicit cleanup. Unrelated suffixes retain normal DNS.
No live root or shared mesh state was changed during this review.

The new infraternal agent accepts missing DNS configuration fields and omits
unrequested response extensions, supporting the old-server/new-node direction.
The opposite direction is blocked by finding 1.

Author evidence

Read J254's full verify-fast-726.log, retained as
author-verify-fast-726.log: all 24 Rust gate rows PASS, including privileged
integration and static builds; protocol tests 50 pass, 0 fail, 77,253 expects.
The reported exit 0 is J254's #266 receipt, not an independent full-gate run.
Reviewer instructions explicitly prohibit rerunning quick/fast.

J254's Linux proof at the reviewed head is #266 comment 84914: allowed FQDN
A/AAAA, ping and strict-host-key SSH, denied/missing equality and clean-stop
removal. Mac root install/system lookup/removal and rootless preservation are
retained in author-mac-dns-resolver.log; the associated Mac build check was
at earlier head 65f445793, in author-mac-build-check-65f.log.

The author's measured 75 microseconds p50, 90 microseconds p99 and 105,328.7
queries/s are not independently remeasured here. The author reports 135,168
bytes per 1,000 names, x86 static size 28,058,672 bytes (+5,640,416) and arm64
25,227,176 (+4,773,896). No latency conclusion is drawn from functional tests.

Raw receipts are attached to the final #266 review comment and retained under
~/.local/state/infraternal-orchestrator/j354/evidence/ on the control host
and ~/infraternal-ops/j354-evidence/ on dev-02.

independent focused receipts

final-focused.log, dev-02 through build-slot, exact reviewed production code plus review tests, serial execution:

J354 classified rotation=None, compact=false
test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 161 filtered out; finished in 0.00s
TCP DNS listener missing: Err(Os { code: 111, kind: ConnectionRefused, message: "Connection refused" })
test result: FAILED. 4 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.62s
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 157 filtered out; finished in 0.00s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 162 filtered out; finished in 0.00s

The two library failures are the capability and nmcli regressions. The DNS failure is missing TCP. The four DNS passes cover privacy, parser recovery, reverse zones, and concurrent UDP load. The final two results are the existing route and identity-shape tests. The status markers in the raw script have a literal n suffix; adversarial cargo exit is 101, route and shape cargo exits are 0.

author full gate, quoted verbatim

Read from the retained raw author-verify-fast-726.log; exit 0 is attributed to J254's receipts in comments 84352 and 84914. Per the common reviewer brief, I did not rerun quick/fast.

+---------------------------+--------------+
| Rust gate                 | Result       |
+---------------------------+--------------+
| Nix Cargo vendoring       | PASS         |
| fmt                       | PASS         |
| migration names           | PASS         |
| migration tooling tests   | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| real-binary fixture paths | PASS         |
| Traefik component manifest | PASS         |
| release component publication | PASS         |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| doc coverage ratchet      | PASS         |
| shape lints off (D351)    | PASS         |
| doc comments on diff      | PASS         |
| fixed test waits          | PASS         |
| privileged integration    | PASS         |
| probe release exclusion   | PASS         |
| nightly fuzz reporter     | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+

Evidence archive: j354-review-evidence.zip, attached to this comment. It includes raw focused logs, the DNS-disabled green control, real nmcli red/green parser output, fuzz compile errors, author gate/Mac receipts, author Linux proof comment, review report and regression patch. Permanent control-host directory: /home/kayg/.local/state/infraternal-orchestrator/j354/evidence/; remote logs: /home/claude/infraternal-ops/j354-evidence/ on dev-02.

All review runs are complete. The sole review target and extracted nmcli packages were removed; cleanup.log records confirmation. Issue remains open.

verdict: REQUEST CHANGES; reviewed head: 726ee694f3b09fc9268b3fe24cb2ede6635c3b55.

J354 independent correctness/security review: **REQUEST CHANGES** reviewed head: `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`. review-only branch: `review-j354` at `7f35610adc4d7159d86fa5cee7d1f86bf2c0f90a`. no merge, deploy, or push to dev. Reviewed `mesh-dns-j254` at `726ee694f3b09fc9268b3fe24cb2ede6635c3b55` against `origin/dev`, merge base `3644950b8e4b5dddeefba350f97a3892189ca193`. Verdict: **REQUEST CHANGES**. Four P2 findings; no P1 established. The review branch contains deliberately failing regressions, not production fixes. ## Findings 1. **P2: enabling DNS loses existing nodes' independent capabilities.** `crates/plugins/mesh/src/node_coordination.rs:41` puts `dns_names` in both negotiation probes. A strict pre-DNS infraternal agent rejects both. Its ordinary `{}` reply omits unrequested capability fields, so the coordinator records rotation support as unknown and compact access as false. If that node has an open rotation, `store.rs:1483` refuses attachment with `ROTATION_IN_PROGRESS`; repeated negotiation cannot recover. Otherwise the false rotation capability closes the mesh-wide rotation gate. Preserve the pre-DNS probes when independently negotiating the DNS extension. `review-repro.log` reproduces this through the actual `read_identity` entry point. `identity-without-dns.log` passes the same test with DNS compiled out. 2. **P2: the NetworkManager split-DNS fallback cannot install its route.** `crates/plugins/mesh/src/dns_integration.rs:310` passes the search-domain list as separate arguments after `ipv4.dns-search`. The real nmcli 1.46 offline parser treats `~internal` as a property and exits 2: `Error: invalid <setting>.<property> '~internal'.` Joining the same domains into one comma-separated value exits 0. `nmcli-property-parser.log` holds both runs; no host connection was modified. Supply one list-valued property argument and test the actual CLI grammar. 3. **P2: the production-promoted DNS service has no TCP listener.** `crates/plugins/mesh/src/dns.rs:399` binds UDP only, including the optional system port. A TCP connect to the running resolver on its configured port returns `ConnectionRefused` (errno 111). TCP clients and clients falling back to TCP cannot resolve mesh names. This was a spike limitation, now relevant to default-on DNS interoperability (RFC 7766). Add a bounded TCP path with the same source/privacy rules. `dns-matrix-serial.log` records the independent reproducer. The earlier parallel run in `dns-focused.log` had a review-harness port collision and is not evidence for this finding. 4. **P2: the DNS fuzz target does not compile.** `cargo check --locked -j3 --manifest-path crates/plugins/mesh/fuzz/Cargo.toml --bin dns` exits 101. The new `dns.rs:520` use of `nix::unistd::Uid` requires the `user` feature missing from the fuzz manifest (E0433). The fixture also lacks `Config.exposure` (E0063, preexisting drift). Repair both so the actual parser target can run. `fuzz-target-check.log` is the raw receipt. ## Checks and limits Focused tests run on dev-02 through build-slot, with its exact Nix cargo launcher checked first, `CARGO_INCREMENTAL=0`, and one target directory. Existing suites in `dns-focused.log`: access updates 5 passed, 3 privileged ignored; configuration 25 passed; DNS 13 passed, 1 privileged ignored. The final serial run in `final-focused.log` has two expected failing library regressions and four passing DNS checks plus the expected TCP failure; cargo exits 101. The six existing route tests and one legacy identity-shape test pass separately, each cargo exit 0. The wrapper exits 0 after collecting all statuses, which is not an overall green result. Its status labels accidentally end with a literal `n`; the cargo summaries and statuses remain intact. The custom UDP matrix compares 16 pairs across two nonlocal source addresses, A/AAAA/ANY/PTR, mixed case and final-dot presentation. Denied and missing answers have equal sizes and identical headers and records after excluding the echoed question: NXDOMAIN, SOA TTL 5. A self-source positive control answers. Nonlocal callers receive NXDOMAIN, not a silent drop. This proves the response behavior using loopback source addresses; no live LAN neighbour test or privacy timing benchmark was performed. Source inspection shows nonlocal callers take a common negative path before any name-table lookup. Denied peer names are omitted from the local table, so local denied and nonexistent lookups have no separate denial-state branch. The deterministic parser sweep completes 100,000 random packets, compression loops, extreme counts, a 65,535-byte packet and a positive recovery query. The socket/parser bound is 4,096 bytes with a 4,097-byte receive sentinel; the worker does not spawn a task per packet. This cheap corpus does not replace the broken fuzz target. Reverse zones are outside mesh authority. Four concurrent clients each complete 1,000 UDP queries and the resolver stops cleanly, recorded in `final-focused.log` without a speed assertion. DNS name hiding does not provide complete peer-address confidentiality: configuration still carries denied peers' keys and addresses in `Config.peers`. That is preexisting architecture, not a newly discovered DNS disclosure. Linux resolved commands target only the mesh link, set `default-route no`, and do not replace global DNS. macOS writes require root, refuse unsafe directories/targets, preserve unrelated resolver files and atomically rename staged files from outside `/etc/resolver`. Cleanup is owned-file scoped. Author clean-stop proofs are evidence of clean stop, not abrupt-stop proof: Drop cannot run after SIGKILL and persistent Mac resolver files can remain until restart or explicit cleanup. Unrelated suffixes retain normal DNS. No live root or shared mesh state was changed during this review. The new infraternal agent accepts missing DNS configuration fields and omits unrequested response extensions, supporting the old-server/new-node direction. The opposite direction is blocked by finding 1. ## Author evidence Read J254's full `verify-fast-726.log`, retained as `author-verify-fast-726.log`: all 24 Rust gate rows PASS, including privileged integration and static builds; protocol tests 50 pass, 0 fail, 77,253 expects. The reported exit 0 is J254's #266 receipt, not an independent full-gate run. Reviewer instructions explicitly prohibit rerunning quick/fast. J254's Linux proof at the reviewed head is #266 comment 84914: allowed FQDN A/AAAA, ping and strict-host-key SSH, denied/missing equality and clean-stop removal. Mac root install/system lookup/removal and rootless preservation are retained in `author-mac-dns-resolver.log`; the associated Mac build check was at earlier head `65f445793`, in `author-mac-build-check-65f.log`. The author's measured 75 microseconds p50, 90 microseconds p99 and 105,328.7 queries/s are not independently remeasured here. The author reports 135,168 bytes per 1,000 names, x86 static size 28,058,672 bytes (+5,640,416) and arm64 25,227,176 (+4,773,896). No latency conclusion is drawn from functional tests. Raw receipts are attached to the final #266 review comment and retained under `~/.local/state/infraternal-orchestrator/j354/evidence/` on the control host and `~/infraternal-ops/j354-evidence/` on dev-02. ## independent focused receipts `final-focused.log`, dev-02 through build-slot, exact reviewed production code plus review tests, serial execution: ```text J354 classified rotation=None, compact=false test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 161 filtered out; finished in 0.00s TCP DNS listener missing: Err(Os { code: 111, kind: ConnectionRefused, message: "Connection refused" }) test result: FAILED. 4 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.62s test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 157 filtered out; finished in 0.00s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 162 filtered out; finished in 0.00s ``` The two library failures are the capability and nmcli regressions. The DNS failure is missing TCP. The four DNS passes cover privacy, parser recovery, reverse zones, and concurrent UDP load. The final two results are the existing route and identity-shape tests. The status markers in the raw script have a literal `n` suffix; adversarial cargo exit is 101, route and shape cargo exits are 0. ## author full gate, quoted verbatim Read from the retained raw `author-verify-fast-726.log`; exit 0 is attributed to J254's receipts in comments 84352 and 84914. Per the common reviewer brief, I did not rerun quick/fast. ```text +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | Nix Cargo vendoring | PASS | | fmt | PASS | | migration names | PASS | | migration tooling tests | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | Traefik component manifest | PASS | | release component publication | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | doc coverage ratchet | PASS | | shape lints off (D351) | PASS | | doc comments on diff | PASS | | fixed test waits | PASS | | privileged integration | PASS | | probe release exclusion | PASS | | nightly fuzz reporter | PASS | | static builds | PASS | +---------------------------+--------------+ ``` Evidence archive: `j354-review-evidence.zip`, attached to this comment. It includes raw focused logs, the DNS-disabled green control, real nmcli red/green parser output, fuzz compile errors, author gate/Mac receipts, author Linux proof comment, review report and regression patch. Permanent control-host directory: `/home/kayg/.local/state/infraternal-orchestrator/j354/evidence/`; remote logs: `/home/claude/infraternal-ops/j354-evidence/` on dev-02. All review runs are complete. The sole review target and extracted nmcli packages were removed; `cleanup.log` records confirmation. Issue remains open. verdict: **REQUEST CHANGES**; reviewed head: `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`.
Author
Owner

J254 review-fix round started on mesh-dns-j254, base/head 726ee694f3b09fc9268b3fe24cb2ede6635c3b55. J354’s four P2s are reproduced in review-j354 and its evidence: old-agent identity negotiation, NetworkManager list grammar, missing TCP service, and DNS fuzz compilation. Plan: add/import each regression red-first, fix capability negotiation and nmcli argv, add bounded TCP framing and privacy tests, repair/run the DNS fuzz target, then run verify quick on dev-02 and verify fast on compute01. After gates, claim the compute01/dev-02 pair for at most 60 minutes for the TCP and old-agent proofs, then release it. Estimate: 3–4 active hours plus build-slot waits. No merge.

J254 review-fix round started on `mesh-dns-j254`, base/head `726ee694f3b09fc9268b3fe24cb2ede6635c3b55`. J354’s four P2s are reproduced in `review-j354` and its evidence: old-agent identity negotiation, NetworkManager list grammar, missing TCP service, and DNS fuzz compilation. Plan: add/import each regression red-first, fix capability negotiation and nmcli argv, add bounded TCP framing and privacy tests, repair/run the DNS fuzz target, then run `verify quick` on dev-02 and `verify fast` on compute01. After gates, claim the compute01/dev-02 pair for at most 60 minutes for the TCP and old-agent proofs, then release it. Estimate: 3–4 active hours plus build-slot waits. No merge.
Author
Owner

J354 finding 1 reproduced red-first. I imported review_j354_identity.rs and ran the strict old-node test on dev-02 against 726ee694f. It fails at read_identity: both capability probes contain dns_names, the old node rejects them, and the fallback reports rotation=None, compact=false despite the fixture supporting both. Raw run: ~/.cache/codex-scratch/J254/evidence/red-identity.log on dev-02. I am keeping the original compact-access/key-rotation probe shapes and querying DNS separately.

J354 finding 1 reproduced red-first. I imported `review_j354_identity.rs` and ran the strict old-node test on dev-02 against `726ee694f`. It fails at `read_identity`: both capability probes contain `dns_names`, the old node rejects them, and the fallback reports `rotation=None`, `compact=false` despite the fixture supporting both. Raw run: `~/.cache/codex-scratch/J254/evidence/red-identity.log` on dev-02. I am keeping the original compact-access/key-rotation probe shapes and querying DNS separately.
Author
Owner

J254 finding 1 fixed and pushed as 7ef1c19ac. read_identity keeps both original compact-access/key-rotation probe payloads unchanged, then asks for dns_names in a separate optional mesh.identity call. An old-node refusal now only leaves DNS support false. The imported regression passed on dev-02 (1 passed, 0 failed) after its expected red; formatting check also passed. Green log: ~/.cache/codex-scratch/J254/evidence/green-identity.log on dev-02.

J254 finding 1 fixed and pushed as `7ef1c19ac`. `read_identity` keeps both original compact-access/key-rotation probe payloads unchanged, then asks for `dns_names` in a separate optional `mesh.identity` call. An old-node refusal now only leaves DNS support false. The imported regression passed on dev-02 (1 passed, 0 failed) after its expected red; formatting check also passed. Green log: `~/.cache/codex-scratch/J254/evidence/green-identity.log` on dev-02.
Author
Owner

J254 finding 2 reproduced against the actual nmcli 1.46 offline parser. The initial automated invocation skipped because dev-02 has no installed nmcli; I corrected that by extracting the exact network-manager/libnm0 packages under ~/.cache/codex-scratch/J254/nmcli and passing NMCLI_TEST_BIN to the regression. No package was installed and no host connection was changed. The red test now fails with invalid <setting>.<property> '~internal'; a manual offline parse of the generated separate values exits 2, while the one comma-separated list exits 0. Evidence: ~/.cache/codex-scratch/J254/evidence/red-nmcli-offline.log and nmcli-manual-red.log on dev-02.

J254 finding 2 reproduced against the actual nmcli 1.46 offline parser. The initial automated invocation skipped because dev-02 has no installed nmcli; I corrected that by extracting the exact `network-manager`/`libnm0` packages under `~/.cache/codex-scratch/J254/nmcli` and passing `NMCLI_TEST_BIN` to the regression. No package was installed and no host connection was changed. The red test now fails with `invalid <setting>.<property> '~internal'`; a manual offline parse of the generated separate values exits 2, while the one comma-separated list exits 0. Evidence: `~/.cache/codex-scratch/J254/evidence/red-nmcli-offline.log` and `nmcli-manual-red.log` on dev-02.
Author
Owner

J254 finding 2 fixed and pushed as ec71bc7ae. networkmanager_route_commands now emits one comma-separated ipv4.dns-search value containing each search and route-only suffix; the existing route-shape test expects one property/value pair. The imported offline-parser regression passed against the extracted nmcli 1.46 binary (1 passed, 0 failed). Green log: ~/.cache/codex-scratch/J254/evidence/green-nmcli-offline.log; the separate raw CLI red/green receipt is nmcli-manual-red.log, both under dev-02 ~/.cache/codex-scratch/J254/evidence/. No live NetworkManager connection was edited.

J254 finding 2 fixed and pushed as `ec71bc7ae`. `networkmanager_route_commands` now emits one comma-separated `ipv4.dns-search` value containing each search and route-only suffix; the existing route-shape test expects one property/value pair. The imported offline-parser regression passed against the extracted nmcli 1.46 binary (1 passed, 0 failed). Green log: `~/.cache/codex-scratch/J254/evidence/green-nmcli-offline.log`; the separate raw CLI red/green receipt is `nmcli-manual-red.log`, both under dev-02 `~/.cache/codex-scratch/J254/evidence/`. No live NetworkManager connection was edited.
Author
Owner

J254 finding 3 reproduced red-first on compute01 at ec71bc7ae. The imported DNS integration suite now fails four TCP cases with ConnectionRefused (TCP availability, framed/privacy response, idle/size bounds, and excess-connection bound); the existing UDP privacy matrix, parser corpus, reverse-zone check and 4,000-query UDP flood pass. Evidence: ~/.cache/codex-scratch/J254/evidence/red-tcp-dns.log. I am adding TCP to the existing poll worker with explicit connection, frame-size and idle bounds.

J254 finding 3 reproduced red-first on compute01 at `ec71bc7ae`. The imported DNS integration suite now fails four TCP cases with `ConnectionRefused` (TCP availability, framed/privacy response, idle/size bounds, and excess-connection bound); the existing UDP privacy matrix, parser corpus, reverse-zone check and 4,000-query UDP flood pass. Evidence: `~/.cache/codex-scratch/J254/evidence/red-tcp-dns.log`. I am adding TCP to the existing poll worker with explicit connection, frame-size and idle bounds.
Author
Owner

Fuzz regression reproduced on dev-02 before changes: cargo check --manifest-path crates/plugins/mesh/fuzz/Cargo.toml --bin dns fails with exactly two errors: nix::unistd::Uid is gated by the missing user feature, and the fixture omits Config.exposure. I am fixing only the fuzz manifest and fixture, then I will compile and run the DNS target.

Fuzz regression reproduced on dev-02 before changes: `cargo check --manifest-path crates/plugins/mesh/fuzz/Cargo.toml --bin dns` fails with exactly two errors: `nix::unistd::Uid` is gated by the missing `user` feature, and the fixture omits `Config.exposure`. I am fixing only the fuzz manifest and fixture, then I will compile and run the DNS target.
Author
Owner

TCP fix pushed as ba13543db. Fresh focused dev-02 result: cargo test -p infraternal-plugin-mesh --features agent --test review_j354_dns: 8 passed, 0 failed (5.16 s). It covers TCP availability/framing, source-filtered D327 denied-name equality, idle and frame bounds, the 64-client cap, parser recovery, UDP flood service, and the J354 wire/reverse-query regressions.

TCP fix pushed as ba13543db. Fresh focused dev-02 result: `cargo test -p infraternal-plugin-mesh --features agent --test review_j354_dns`: 8 passed, 0 failed (5.16 s). It covers TCP availability/framing, source-filtered D327 denied-name equality, idle and frame bounds, the 64-client cap, parser recovery, UDP flood service, and the J354 wire/reverse-query regressions.
Author
Owner

Fuzz runner setup exposed one environment issue: cargo-fuzz was installed, but the Nix shell exported the pinned stable compiler, so its -Zsanitizer=address build rejected the stable rustc. The repository fuzz runner explicitly sets RUSTC and RUSTDOC to nightly paths; I am using that same setup for the timed DNS run.

Fuzz runner setup exposed one environment issue: cargo-fuzz was installed, but the Nix shell exported the pinned stable compiler, so its `-Zsanitizer=address` build rejected the stable rustc. The repository fuzz runner explicitly sets `RUSTC` and `RUSTDOC` to nightly paths; I am using that same setup for the timed DNS run.
Author
Owner

DNS fuzz target now builds and ran under nightly ASan on dev-02: Done 5507803 runs in 181 second(s), exit 0, 0 artifacts, peak RSS 107 MB. The earlier stable-rustc attempt is retained separately in the evidence directory; the successful run used the repo script’s nightly RUSTC/RUSTDOC setup.

DNS fuzz target now builds and ran under nightly ASan on dev-02: `Done 5507803 runs in 181 second(s)`, exit 0, 0 artifacts, peak RSS 107 MB. The earlier stable-rustc attempt is retained separately in the evidence directory; the successful run used the repo script’s nightly `RUSTC`/`RUSTDOC` setup.
Author
Owner

Gate invocation correction: my first quick/fast attempts started scripts/verify from /home/claude; Nix develop preserves the caller cwd, so these failed before reaching branch checks (missing Cargo.toml/scripts and origin/dev). I am rerunning each from its clean j254-gates-20261107 worktree and retaining the invalid invocation logs separately.

Gate invocation correction: my first quick/fast attempts started `scripts/verify` from `/home/claude`; Nix develop preserves the caller cwd, so these failed before reaching branch checks (missing Cargo.toml/scripts and origin/dev). I am rerunning each from its clean `j254-gates-20261107` worktree and retaining the invalid invocation logs separately.
Author
Owner

Correctly invoked verify quick reached the branch checks but failed its D345 diff-doc row: the imported review_j354_integration and review_j354_identity test modules need module docs in dns_integration.rs and node_coordination.rs. I am adding those test-only docs, then rerunning quick and fast on the updated head.

Correctly invoked `verify quick` reached the branch checks but failed its D345 diff-doc row: the imported `review_j354_integration` and `review_j354_identity` test modules need module docs in dns_integration.rs and node_coordination.rs. I am adding those test-only docs, then rerunning quick and fast on the updated head.
Author
Owner

D345 follow-up pushed as 914634072. Added docs to both imported test modules; python3 scripts/test-diff-practices.py on dev-02 passes 12/12. I will refresh the gate worktrees to this head and rerun quick/fast.

D345 follow-up pushed as 914634072. Added docs to both imported test modules; `python3 scripts/test-diff-practices.py` on dev-02 passes 12/12. I will refresh the gate worktrees to this head and rerun quick/fast.
Author
Owner

The corrected verify quick run on dev-02 passed all rows, including workspace clippy and changed-package lib/bin tests. The 34 ENOENT failures were fixture paths under ../../target while CARGO_TARGET_DIR pointed at a separate worktree; a symlink makes both use the same single target. No mesh feature-set failure reproduced; the privileged feature matrix remains covered in fast.

The corrected `verify quick` run on dev-02 passed all rows, including workspace clippy and changed-package lib/bin tests. The 34 ENOENT failures were fixture paths under `../../target` while CARGO_TARGET_DIR pointed at a separate worktree; a symlink makes both use the same single target. No mesh feature-set failure reproduced; the privileged feature matrix remains covered in fast.
Author
Owner

J254 review repair status: the four J354 P2 fixes are pushed through 5d90573f9. The requested dev-02 verify quick is queued at position 2 on the required build-slot; its log will be /home/claude/infraternal-ops/j254-evidence/verify-quick-5d90573f9.log. An earlier compute01 verify fast run on 6a0d512f6 is still finishing; I will record it as superseded and run the final gate at the current head. Next are the UDP latency comparison and a short live TCP plus pre-DNS attachment proof under the mesh claim.

J254 review repair status: the four J354 P2 fixes are pushed through 5d90573f9. The requested dev-02 verify quick is queued at position 2 on the required build-slot; its log will be /home/claude/infraternal-ops/j254-evidence/verify-quick-5d90573f9.log. An earlier compute01 verify fast run on 6a0d512f6 is still finishing; I will record it as superseded and run the final gate at the current head. Next are the UDP latency comparison and a short live TCP plus pre-DNS attachment proof under the mesh claim.
Author
Owner

Finding: after fetching origin, origin/dev is 6ffe37e and mesh-dns-j254 is 5d90573f9, with common ancestor 3644950b8e (15 commits ahead, 77 behind). Per the common build brief, the final fast gate must follow upstream integration; I will merge origin/dev after the queued quick and run fast on the integrated head. The E0063 missing access_ssh text in the earlier fast log is the expected captured sample asserted by scripts/test-verify-privileged.py:501, not a compile failure.

Finding: after fetching origin, origin/dev is 6ffe37e and mesh-dns-j254 is 5d90573f9, with common ancestor 3644950b8e4b (15 commits ahead, 77 behind). Per the common build brief, the final fast gate must follow upstream integration; I will merge origin/dev after the queued quick and run fast on the integrated head. The E0063 missing access_ssh text in the earlier fast log is the expected captured sample asserted by scripts/test-verify-privileged.py:501, not a compile failure.
Author
Owner

Correction: I integrated origin/dev locally as 6f0a07af1 while the queued quick remains isolated on its detached 5d90573f9 checkout. I will run the final fast gate on the integrated head and push it once that gate is verified.

Correction: I integrated origin/dev locally as 6f0a07af1 while the queued quick remains isolated on its detached 5d90573f9 checkout. I will run the final fast gate on the integrated head and push it once that gate is verified.
Author
Owner

Final integrated verify fast started on compute01 at 6f0a07af1. It is running through build-slot in /home/claude/worktrees/j254-gates-20261107; log: /home/claude/.cache/codex-scratch/J254/evidence/verify-fast-integrated.log. The older 6a0d512f6 run exited 1 with clippy (101) and D345 docs (1) failing before their fixes; its privileged row and static builds passed. The requested quick remains foreground-queued at position 2 on dev-02.

Final integrated verify fast started on compute01 at 6f0a07af1. It is running through build-slot in `/home/claude/worktrees/j254-gates-20261107`; log: `/home/claude/.cache/codex-scratch/J254/evidence/verify-fast-integrated.log`. The older 6a0d512f6 run exited 1 with clippy (101) and D345 docs (1) failing before their fixes; its privileged row and static builds passed. The requested quick remains foreground-queued at position 2 on dev-02.
Author
Owner

J254 review fixes update

Head 6f0a07af1 is pushed with the optional DNS capability extension, corrected nmcli search-domain argument, bounded TCP DNS path, and fuzz target repairs. Focused TCP tests passed 8/8. The DNS fuzz target ran 181 seconds: 5,507,803 executions, exit 0, 107 MB peak RSS.

Current gates: compute01 verify fast is still in tests. dev-02 verify quick remains queued at position 2 and has not started. I’ll post the final gate output, UDP before/after latency measurement, and the TCP plus pre-DNS attachment live proof when complete.

J254 review fixes update Head 6f0a07af1 is pushed with the optional DNS capability extension, corrected nmcli search-domain argument, bounded TCP DNS path, and fuzz target repairs. Focused TCP tests passed 8/8. The DNS fuzz target ran 181 seconds: 5,507,803 executions, exit 0, 107 MB peak RSS. Current gates: compute01 verify fast is still in tests. dev-02 verify quick remains queued at position 2 and has not started. I’ll post the final gate output, UDP before/after latency measurement, and the TCP plus pre-DNS attachment live proof when complete.
Author
Owner

J254 gate update

The corrected dev-02 verify quick completed with exit 0; its Rust pre-check passed. The first queued invocation exited 127 before starting because it lacked the checkout working directory. The corrected foreground run used one build slot. Its target and /tmp/j254-kernel-node-aarch64-musl-726 are now removed; dev-02 has 82 GB free.

The integrated compute01 verify fast at 6f0a07af1 completed with exit 0. Its Rust gate summary marks every row PASS, including privileged integration (96/96 rows), release exclusion, nightly fuzz reporter, and static builds.

The UDP-only baseline and TCP-enabled current benchmark binaries are now building on compute01.

J254 gate update The corrected dev-02 verify quick completed with exit 0; its Rust pre-check passed. The first queued invocation exited 127 before starting because it lacked the checkout working directory. The corrected foreground run used one build slot. Its target and /tmp/j254-kernel-node-aarch64-musl-726 are now removed; dev-02 has 82 GB free. The integrated compute01 verify fast at 6f0a07af1 completed with exit 0. Its Rust gate summary marks every row PASS, including privileged integration (96/96 rows), release exclusion, nightly fuzz reporter, and static builds. The UDP-only baseline and TCP-enabled current benchmark binaries are now building on compute01.
Author
Owner

J254 UDP latency and memory comparison

Measured on perf-01 with /usr/local/bin/perf-run, 8 clients, 80,000 UDP queries per run, 1,000 names. Four runs were interleaved per source. The binaries were built on compute01 with the same lockfile, swapping only dns.rs: UDP-only source ec71bc7ae versus TCP-enabled source 6f0a07af1.

Per-pair p50/p99 in microseconds:

  • pair 1: UDP-only 75.892/99.056; TCP-enabled 55.293/75.442
  • pair 2: UDP-only 147.467/335.207; TCP-enabled 82.084/102.101
  • pair 3: UDP-only 114.675/316.063; TCP-enabled 54.943/75.151
  • pair 4: UDP-only 144.200/230.141; TCP-enabled 78.156/101.169

Median p50/p99: UDP-only 129.438/273.102 us; TCP-enabled 66.725/88.306 us. The TCP-enabled source was lower in all four pairs.

Median RSS delta for 1,000 names: UDP-only 655,360 bytes; TCP-enabled 657,408 bytes (+2,048 bytes). Each run exited 0. Raw before/after receipts are under /home/claude/infraternal-ops/j254-evidence/dns-ab-20261107-{udp-a1,tcp-b1,udp-a2,tcp-b2,udp-a3,tcp-b3,udp-a4,tcp-b4}.

J254 UDP latency and memory comparison Measured on perf-01 with /usr/local/bin/perf-run, 8 clients, 80,000 UDP queries per run, 1,000 names. Four runs were interleaved per source. The binaries were built on compute01 with the same lockfile, swapping only dns.rs: UDP-only source ec71bc7ae versus TCP-enabled source 6f0a07af1. Per-pair p50/p99 in microseconds: - pair 1: UDP-only 75.892/99.056; TCP-enabled 55.293/75.442 - pair 2: UDP-only 147.467/335.207; TCP-enabled 82.084/102.101 - pair 3: UDP-only 114.675/316.063; TCP-enabled 54.943/75.151 - pair 4: UDP-only 144.200/230.141; TCP-enabled 78.156/101.169 Median p50/p99: UDP-only 129.438/273.102 us; TCP-enabled 66.725/88.306 us. The TCP-enabled source was lower in all four pairs. Median RSS delta for 1,000 names: UDP-only 655,360 bytes; TCP-enabled 657,408 bytes (+2,048 bytes). Each run exited 0. Raw before/after receipts are under /home/claude/infraternal-ops/j254-evidence/dns-ab-20261107-{udp-a1,tcp-b1,udp-a2,tcp-b2,udp-a3,tcp-b3,udp-a4,tcp-b4}.
Author
Owner

J254 resumed the final live proof on mesh-dns-j254 at 6f0a07af1.

Built the pre-DNS aarch64 test agent from origin/dev at 6ffe37e067. Its static binary SHA-256 is 1faec5986e978f8135886fc04f2bf43509828533786ee3cb347d8c8b58ec6697. The live helper imports and TCP DNS framing are checked on both hosts. The mesh claim is free; I’m taking it now for the pre-DNS attachment, TCP A/AAAA query, and shutdown/resolver cleanup check.

J254 resumed the final live proof on mesh-dns-j254 at 6f0a07af1. Built the pre-DNS aarch64 test agent from origin/dev at 6ffe37e067d8fa7b090ea83c21b84b77937f76d9. Its static binary SHA-256 is 1faec5986e978f8135886fc04f2bf43509828533786ee3cb347d8c8b58ec6697. The live helper imports and TCP DNS framing are checked on both hosts. The mesh claim is free; I’m taking it now for the pre-DNS attachment, TCP A/AAAA query, and shutdown/resolver cleanup check.
Author
Owner

The first claimed run stopped at the live fixture's assertion that the pre-DNS dev-02 node must report key_rotation=true. Investigation shows the fixture was too strict: the old build defaults to GotaTun, which deliberately reports rotation unsupported until that engine implements it; its compact_access response was true. The run did not attempt attachment or TCP DNS. Cleanup verified the claim is free, infraternal0 is absent on both hosts, and systemd-resolved has no infraternal0 route. I’ve corrected the fixture to record the node’s actual boolean capability and continue; the J354 regression test still requires a strict old node that supports rotation to retain true.

The first claimed run stopped at the live fixture's assertion that the pre-DNS dev-02 node must report key_rotation=true. Investigation shows the fixture was too strict: the old build defaults to GotaTun, which deliberately reports rotation unsupported until that engine implements it; its compact_access response was true. The run did not attempt attachment or TCP DNS. Cleanup verified the claim is free, infraternal0 is absent on both hosts, and systemd-resolved has no infraternal0 route. I’ve corrected the fixture to record the node’s actual boolean capability and continue; the J354 regression test still requires a strict old node that supports rotation to retain true.
Author
Owner

The second claimed attempt acquired J254 2026-11-07T23:30:45Z, then stopped before starting either mesh node: compute01 became unreachable (SSH reports “No route to host”) while the harness checked J207’s server port. Dev-02 still has no infraternal0. The harness could not reach compute01 to release the claim, so release is unconfirmed. I’ll retry the exact claim cleanup as soon as compute01 is reachable. No other VM pair is being used.

The second claimed attempt acquired J254 2026-11-07T23:30:45Z, then stopped before starting either mesh node: compute01 became unreachable (SSH reports “No route to host”) while the harness checked J207’s server port. Dev-02 still has no infraternal0. The harness could not reach compute01 to release the claim, so release is unconfirmed. I’ll retry the exact claim cleanup as soon as compute01 is reachable. No other VM pair is being used.
Author
Owner

The 60-minute J254 claim window elapsed while compute01 remained unreachable. The second attempt stopped before starting either mesh node; dev-02 still has no infraternal0, but I cannot verify or remove the claim file because SSH returns “No route to host.” Treat the TCP and old-agent live proof as incomplete. On recovery, first read /home/claude/mesh-vm.claim; if it still equals J254 2026-11-07T23:30:45Z, remove only that exact claim and verify the interface/resolver are absent before acquiring a fresh claim. The last successful preflight showed no listener on port 45680; J207’s server was not touched.

The 60-minute J254 claim window elapsed while compute01 remained unreachable. The second attempt stopped before starting either mesh node; dev-02 still has no infraternal0, but I cannot verify or remove the claim file because SSH returns “No route to host.” Treat the TCP and old-agent live proof as incomplete. On recovery, first read /home/claude/mesh-vm.claim; if it still equals `J254 2026-11-07T23:30:45Z`, remove only that exact claim and verify the interface/resolver are absent before acquiring a fresh claim. The last successful preflight showed no listener on port 45680; J207’s server was not touched.
Author
Owner

compute01 is reachable again; it is up with 264 GB free, the mesh claim is free, and infraternal0 is absent. dev-02 has the pre-DNS binary. The previous stopped attempt exposed a fixture environment issue: that old binary defaults to GotaTun, where rotation is unavailable. The existing per-node INFRATERNAL_MESH_ENGINE=kernel override is supported, so the live compatibility proof will require key_rotation=true and compact_access=true from the pre-DNS binary, then attach it and query DNS over TCP. I am rebuilding the small coordinator fixture on compute01 and restaging the proof helper, then I will claim the compute01 + dev-02 pair for the live run and release it immediately.

compute01 is reachable again; it is up with 264 GB free, the mesh claim is free, and infraternal0 is absent. dev-02 has the pre-DNS binary. The previous stopped attempt exposed a fixture environment issue: that old binary defaults to GotaTun, where rotation is unavailable. The existing per-node INFRATERNAL_MESH_ENGINE=kernel override is supported, so the live compatibility proof will require key_rotation=true and compact_access=true from the pre-DNS binary, then attach it and query DNS over TCP. I am rebuilding the small coordinator fixture on compute01 and restaging the proof helper, then I will claim the compute01 + dev-02 pair for the live run and release it immediately.
Author
Owner

The first claimed attempt stopped before creating either mesh node: the staged compute01 fixture lacked latency_proxy.py, so both runner processes exited at import time. I confirmed no runner remains, infraternal0 is absent on both machines, and resolvectl's per-link domain query reports no route on either host. The claim is being released after that cleanup check; I am staging the missing helper and will retry the TCP lookup plus pre-DNS kernel-agent attachment.

The first claimed attempt stopped before creating either mesh node: the staged compute01 fixture lacked latency_proxy.py, so both runner processes exited at import time. I confirmed no runner remains, infraternal0 is absent on both machines, and resolvectl's per-link domain query reports no route on either host. The claim is being released after that cleanup check; I am staging the missing helper and will retry the TCP lookup plus pre-DNS kernel-agent attachment.
Author
Owner

The retry acquired the compute01 + dev-02 claim. The pre-DNS binary, run with its existing kernel engine override, reports key_rotation=true and compact_access=true on both identity probes and refuses the DNS extension as expected. The current DNS node advertises the extension. The live proof is proceeding through attachment, TCP A/AAAA lookup, shutdown, and resolver cleanup.

The retry acquired the compute01 + dev-02 claim. The pre-DNS binary, run with its existing kernel engine override, reports key_rotation=true and compact_access=true on both identity probes and refuses the DNS extension as expected. The current DNS node advertises the extension. The live proof is proceeding through attachment, TCP A/AAAA lookup, shutdown, and resolver cleanup.
Author
Owner

The second TCP attempt showed the source privacy rule working as designed: a TCP query from dev-02 (.2) directly to compute01’s resolver (.1) returned NXDOMAIN. dns.rs::response_for_source answers names only when the source is the resolver node’s own mesh address; remote peer queries are hidden. The nodes attached, old-agent capabilities were true, and the mesh ping step completed before that query. I am changing the live check to query an allowed peer name through compute01’s own mesh-addressed resolver, and to verify that an over-mesh query from dev-02 receives the same NXDOMAIN/SOA for an existing name and a missing name. Both nodes are down, links and resolver routes are absent, and the claim is free.

The second TCP attempt showed the source privacy rule working as designed: a TCP query from dev-02 (.2) directly to compute01’s resolver (.1) returned NXDOMAIN. `dns.rs::response_for_source` answers names only when the source is the resolver node’s own mesh address; remote peer queries are hidden. The nodes attached, old-agent capabilities were true, and the mesh ping step completed before that query. I am changing the live check to query an allowed peer name through compute01’s own mesh-addressed resolver, and to verify that an over-mesh query from dev-02 receives the same NXDOMAIN/SOA for an existing name and a missing name. Both nodes are down, links and resolver routes are absent, and the claim is free.
Author
Owner

The corrected TCP query on compute01 succeeded for infraternal-dev-02-std.internal and returned A 10.254.0.2 plus the coordinator-assigned AAAA fda5:ee0a:5ba3::2. The attempt stopped because the temporary harness still expected an older IPv6 prefix; the legacy node had attached with rotation and compact capabilities true and the ping stage completed. I verified shutdown: the claim is free, and both hosts have no infraternal0 link or resolver domain. The harness now compares DNS answers with the coordinator fixture’s assigned addresses and will rerun the TCP privacy comparison.

The corrected TCP query on compute01 succeeded for infraternal-dev-02-std.internal and returned A 10.254.0.2 plus the coordinator-assigned AAAA fda5:ee0a:5ba3::2. The attempt stopped because the temporary harness still expected an older IPv6 prefix; the legacy node had attached with rotation and compact capabilities true and the ping stage completed. I verified shutdown: the claim is free, and both hosts have no infraternal0 link or resolver domain. The harness now compares DNS answers with the coordinator fixture’s assigned addresses and will rerun the TCP privacy comparison.
Author
Owner

Final live proof on compute01 + dev-02. Claim: J254 2026-11-08T03:37:58Z; released and verified free. Branch head remains 6f0a07af1d, clean.

  • The pre-DNS binary on dev-02, run with INFRATERNAL_MESH_ENGINE=kernel, returned key_rotation=true and compact_access=true on both identity probes; it refused the optional DNS probe. The current node advertised DNS. Both configurations applied, both mesh states were On, and the old node pinged compute01 over the mesh (2/2 replies).
  • TCP DNS on compute01's mesh address resolved infraternal-dev-02-std.internal: A 10.254.0.2 and AAAA fd56:b869:961c::2 (TTL 30). Over the mesh, dev-02 queried that existing name and a nonexistent name via TCP; both returned NXDOMAIN with no answers, identical response headers, and identical decoded SOA fields.
  • Both daemons shut down cleanly (off=true, exit 0). Post-run checks found no infraternal0 link or resolver domain on either host, no claim file, no scratch directory, and no listener on J207's port 45680.

The earlier #266 notes document the harness-only corrections during the proof. Existing quick/fast and privileged gate results remain recorded above; this live proof made no tracked code changes.

Final live proof on compute01 + dev-02. Claim: J254 2026-11-08T03:37:58Z; released and verified free. Branch head remains 6f0a07af1daaaf657b716faa4bd51f05396c632e, clean. - The pre-DNS binary on dev-02, run with INFRATERNAL_MESH_ENGINE=kernel, returned key_rotation=true and compact_access=true on both identity probes; it refused the optional DNS probe. The current node advertised DNS. Both configurations applied, both mesh states were On, and the old node pinged compute01 over the mesh (2/2 replies). - TCP DNS on compute01's mesh address resolved infraternal-dev-02-std.internal: A 10.254.0.2 and AAAA fd56:b869:961c::2 (TTL 30). Over the mesh, dev-02 queried that existing name and a nonexistent name via TCP; both returned NXDOMAIN with no answers, identical response headers, and identical decoded SOA fields. - Both daemons shut down cleanly (off=true, exit 0). Post-run checks found no infraternal0 link or resolver domain on either host, no claim file, no scratch directory, and no listener on J207's port 45680. The earlier #266 notes document the harness-only corrections during the proof. Existing quick/fast and privileged gate results remain recorded above; this live proof made no tracked code changes.
Author
Owner

J360 independent re-review started on review-j360 at 6f0a07af1d against origin/dev. Plan (about 90 active minutes plus slot waits): inspect four P2 repairs and capability compatibility both directions; run focused DNS/identity/nmcli suites and fuzz on dev-02 through checked build-slot launcher; inspect/re-measure UDP claim and stress TCP privacy/resource bounds; post verdict and durable evidence. No merge/deploy/dev push. Per common reviewer brief, no quick/fast rerun.

J360 independent re-review started on review-j360 at 6f0a07af1daaaf657b716faa4bd51f05396c632e against origin/dev. Plan (about 90 active minutes plus slot waits): inspect four P2 repairs and capability compatibility both directions; run focused DNS/identity/nmcli suites and fuzz on dev-02 through checked build-slot launcher; inspect/re-measure UDP claim and stress TCP privacy/resource bounds; post verdict and durable evidence. No merge/deploy/dev push. Per common reviewer brief, no quick/fast rerun.
Author
Owner

J360 P2 finding reproduced: TCP has an idle timeout but no absolute frame/read deadline. In review_j360_partial_frame_has_an_absolute_read_deadline, 64 clients advertise 4096-byte frames and drip one byte/second for seven seconds. All still occupy slots; the 65th connection gets EOF, while the original partial reader times out (WouldBlock), not EOF. Cargo exits 101. dns.rs:272 resets last_activity on every read, and :615 expires only last_activity; an outsider can monopolize all slots before source/privacy processing. Add a deadline independent of incremental byte activity. Evidence: dev-02 /home/claude/infraternal-ops/j360-evidence/repro.log. Existing J354 regressions and explicit nmcli offline parser pass in the same log. Review continues on bounded worker service and performance receipts.

J360 P2 finding reproduced: TCP has an idle timeout but no absolute frame/read deadline. In review_j360_partial_frame_has_an_absolute_read_deadline, 64 clients advertise 4096-byte frames and drip one byte/second for seven seconds. All still occupy slots; the 65th connection gets EOF, while the original partial reader times out (WouldBlock), not EOF. Cargo exits 101. dns.rs:272 resets last_activity on every read, and :615 expires only last_activity; an outsider can monopolize all slots before source/privacy processing. Add a deadline independent of incremental byte activity. Evidence: dev-02 /home/claude/infraternal-ops/j360-evidence/repro.log. Existing J354 regressions and explicit nmcli offline parser pass in the same log. Review continues on bounded worker service and performance receipts.
Author
Owner

J360 second P2 reproduced: read_tcp_request loops across malformed/out-of-zone frames until WouldBlock, with no per-turn byte/frame budget. A single continuously writing nonlocal TCP client sending framed invalid 12-byte packets prevented the shared worker from servicing a valid UDP lookup. The UDP socket timed out during the flood, then received its queued 49-byte answer immediately after the TCP writer stopped. Test review_j360_invalid_tcp_stream_yields_to_udp exits 101. Evidence: dev-02 /home/claude/infraternal-ops/j360-evidence/stress.log. Budget TCP reads/parses per poll turn (including error/out-of-zone outcomes) so UDP, deadlines and shutdown remain serviceable. This is separate from the partial-frame deadline finding in 86254. No production code changed.

J360 second P2 reproduced: read_tcp_request loops across malformed/out-of-zone frames until WouldBlock, with no per-turn byte/frame budget. A single continuously writing nonlocal TCP client sending framed invalid 12-byte packets prevented the shared worker from servicing a valid UDP lookup. The UDP socket timed out during the flood, then received its queued 49-byte answer immediately after the TCP writer stopped. Test review_j360_invalid_tcp_stream_yields_to_udp exits 101. Evidence: dev-02 /home/claude/infraternal-ops/j360-evidence/stress.log. Budget TCP reads/parses per poll turn (including error/out-of-zone outcomes) so UDP, deadlines and shutdown remain serviceable. This is separate from the partial-frame deadline finding in 86254. No production code changed.
Author
Owner

J360 independent correctness/security re-review: REQUEST CHANGES

Reviewed mesh-dns-j254 at 6f0a07af1daaaf657b716faa4bd51f05396c632e against origin/dev (fetched tip 7dc56b8e89ab8a9ccbfa59de3f1cfa8d776ba23f, merge base 6ffe37e067d8fa7b090ea83c21b84b77937f76d9). Read J354 #266 comment 85284 and J254's subsequent fixes/live proof through 86238. Review-only branch review-j360 at 53c0441247f9095304be699eab2cdb0a747bc0a9 contains two deliberately failing tests and no production edits. No merge, deploy, or dev push.

Findings

P1: none established. P2: two. P3: no additional code finding.

  1. P2: partial TCP frames have no absolute read deadline, permitting slowloris slot exhaustion. crates/plugins/mesh/src/dns.rs:273 resets last_activity on every successful byte read; :199 and :614 implement only an inactivity deadline. There is no frame-start deadline. A client can occupy a slot by dripping bytes below five seconds, including before source validation. Independent test review_j360_partial_frame_has_an_absolute_read_deadline opens 64 connections, advertises 4,096-byte frames, and drips one byte/second for seven seconds. The original connection remains open (read returns WouldBlock), and the 65th connection is closed (EOF). All 64 slots remain unavailable to real clients. Add a frame/header completion deadline independent of incremental activity and cover slot recovery under drip traffic. Evidence: repro.log, repeated after formatting in final-repro.log; cargo exit 101. Finding first posted in 86254.

  2. P2: one continuously readable invalid TCP stream starves the shared UDP worker. dns.rs:231-285 loops over reads and restarts parsing after malformed/out-of-zone frames without a byte/frame budget. With a continuously writing client, it need not reach WouldBlock or return to poll. That also prevents the outer loop from checking shutdown and client deadlines. Independent test review_j360_invalid_tcp_stream_yields_to_udp sends batches of length-prefixed invalid 12-byte packets from a nonlocal source. A valid self-source UDP query times out during the flood; after stopping the writer, that exact queued query receives its 49-byte answer. Repeated twice, including the final formatted tests. Add a per-turn read/parse budget covering error and out-of-zone results, then return to the outer worker. Evidence: stress.log, final-repro.log; cargo exit 101. Finding first posted in 86259. This is a service-liveness reproducer, not a functional-test latency benchmark.

Final independent regression result:

test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 8 filtered out; finished in 9.45s
J360_FINAL_REPRO_EXIT=101

J354 repairs and focused validation

All build/test work ran on dev-02 through its build-slot, after reading the launcher and checking nix develop . -c cargo --version (cargo 1.99.0). Used CARGO_INCREMENTAL=0 and one target directory. Specific suites, serial socket tests:

  • --lib review_j: 3 passed. With NMCLI_TEST_BIN explicitly selecting the extracted real nmcli 1.46 binary and its libnm library, all 3 passed again without the parser skip.
  • --lib ordinary_identity_reply: 1 passed.
  • --test access_updates: 5 passed, 3 privileged ignored.
  • --test configuration: 25 passed.
  • --test dns: 13 passed, 1 privileged ignored.
  • Original --test review_j354_dns: 8 passed, including all J354 original DNS regressions and J254's TCP additions. The imported identity and nmcli tests retain J354's assertions; their changes are import/lint compatibility and added DNS/real-parser checks.

Raw receipts: focused.log, repro.log. Privileged tests were not rerun in this review; J254's live proof is #266 comment 86238.

Capabilities: read_identity preserves both pre-DNS probes byte-for-byte, then calls read_dns_names independently. The strict old-node fixture now reports rotation=Some(true), compact=true, DNS=false. J254's live kernel-engine proof separately reports both pre-DNS capabilities true and successful attachment/ping. In the opposite direction, IdentityProbe accepts the legacy requests, identity_response emits DNS only when requested, false extension fields are omitted, and Config/Chunk DNS fields have serde defaults. Thus a new node's reply does not add unknown DNS fields to an old server's strict decoder. The legacy reply-shape test passes. I did not deploy an old server.

nmcli: production emits one comma-separated ipv4.dns-search value. It currently emits no ipv6.dns-search property. The real offline parser accepts the generated command. A separate actual nmcli invocation supplied the same comma-separated list to BOTH IPv4 and IPv6 properties and exited 0, rendering both correctly. Receipt: nmcli-both-families.log. No live connection was modified.

TCP bounds/privacy: 64 active clients globally across listeners; 4,096-byte query cap (stricter than the u16 framing ceiling of 65,535); one bounded input/output frame; nonblocking reads/writes; five-second inactivity timeout. Existing cap/oversize/idle tests pass. Those bounds do not repair the two findings above. TCP and UDP call the same response_for_source: nonlocal sources receive NXDOMAIN/SOA before any name lookup, exactly as UDP; they are not rejected at accept time. Local policy-denied names are omitted from the same name map used for nonexistent names. There is no separate denied-name branch or lookup for nonlocal callers, so TCP adds no name-existence-dependent timing path. Wire equality passes; no constant-time guarantee or independent TCP timing distribution is claimed. J254's live TCP equality proof is in 86238. Loopback tests do not independently prove host firewall admission.

Fuzz: target repairs compile. Independent nightly ASan cargo-fuzz run on the reviewed production source completed Done 910117 runs in 31 second(s), with no artifacts (peak reported RSS 103 MB), max input length 65,535. Receipt: fuzz.log. Also read and retained J254's successful Done 5507803 runs in 181 second(s) receipt as author-fuzz-180s.log. This target exercises the parser/response boundary, not the TCP state machine; the new socket stress tests cover the latter.

UDP performance claim

Independently reran the supplied static before/after binaries on perf-01 through /usr/local/bin/perf-run, reversing initial order and interleaving three runs of each. Each reports 8 clients, 80,000 queries and 1,000 names; all exit 0. Retained stdout AND before/after machine-state receipts under perf/.

Run UDP-only p50/p99, us TCP-enabled p50/p99, us
1 152.906 / 325.129 55.063 / 73.929
2 143.379 / 271.460 80.291 / 105.728
3 142.918 / 225.302 51.426 / 129.642

Median per-run p50/p99: 143.379/271.460 versus 55.063/105.728 us. The reported improvement is repeatable for these binaries, not merely a lucky original sample or an artefact of taking four-run medians. The saved dns.rs source hashes match ec71bc7ae and the reviewed head, and the retained author build script uses the same lockfile. However, the temporary benchmark example source was removed by that script, so I could not audit its timing boundaries or independently rebuild it. This is a limit on attributing the gain to TCP implementation work: the three-CPU, eight-client local round-trip workload includes scheduling and queueing, not isolated DNS processing or real-mesh latency. The author's named original perf directories contain machine snapshots but no query-output log; my rerun retains that output. No general production speedup is certified. More importantly, unloaded UDP numbers do not cover finding 2's demonstrated UDP outage under TCP input.

Author full gate (not independently rerun)

Per the common reviewer brief, no quick/fast rerun. Read the raw integrated-head author receipt from compute01 and retained it as author-verify-fast-integrated.log. Author exit 0 is recorded in #266 comment 85897. Table below quoted verbatim:

+---------------------------+--------------+
| Rust gate                 | Result       |
+---------------------------+--------------+
| Nix Cargo vendoring       | PASS         |
| fmt                       | PASS         |
| migration names           | PASS         |
| migration tooling tests   | PASS         |
| VT native fmt             | PASS         |
| CLI plugin compile-out    | PASS         |
| clippy                    | PASS         |
| SSH runtime test helper   | PASS         |
| real-binary fixture paths | PASS         |
| Traefik component manifest | PASS         |
| release component publication | PASS         |
| test                      | PASS         |
| SSH inode reuse fixture   | PASS         |
| shim process-per-user     | PASS         |
| cargo-deny                | PASS         |
| gate architecture         | PASS         |
| doc coverage ratchet      | PASS         |
| shape lints off (D351)    | PASS         |
| doc comments on diff      | PASS         |
| fixed test waits          | PASS         |
| privileged integration    | PASS         |
| probe release exclusion   | PASS         |
| nightly fuzz reporter     | PASS         |
| static builds             | PASS         |
+---------------------------+--------------+

Evidence and cleanup

Attached archive contains raw reviewer logs, author gate/fuzz receipts, perf stdout and machine-state receipts, build script, and regression patch. Permanent control-host directory: /home/kayg/.local/state/infraternal-orchestrator/j360/evidence/. Reviewer logs also remain at /home/claude/infraternal-ops/j360-evidence/ on dev-02; performance logs at the same path on perf-01. The single review target was deleted, own runners have exited, and copied benchmark executables were removed from perf-01. cleanup.log records the runner/target check. No live mesh claim, interface, resolver route, or server was changed.

verdict: REQUEST CHANGES; reviewed head: 6f0a07af1daaaf657b716faa4bd51f05396c632e; review branch head: 53c0441247f9095304be699eab2cdb0a747bc0a9.

J360 independent correctness/security re-review: **REQUEST CHANGES** Reviewed `mesh-dns-j254` at `6f0a07af1daaaf657b716faa4bd51f05396c632e` against `origin/dev` (fetched tip `7dc56b8e89ab8a9ccbfa59de3f1cfa8d776ba23f`, merge base `6ffe37e067d8fa7b090ea83c21b84b77937f76d9`). Read J354 #266 comment 85284 and J254's subsequent fixes/live proof through 86238. Review-only branch `review-j360` at `53c0441247f9095304be699eab2cdb0a747bc0a9` contains two deliberately failing tests and no production edits. No merge, deploy, or dev push. ## Findings **P1: none established. P2: two. P3: no additional code finding.** 1. **P2: partial TCP frames have no absolute read deadline, permitting slowloris slot exhaustion.** `crates/plugins/mesh/src/dns.rs:273` resets `last_activity` on every successful byte read; `:199` and `:614` implement only an inactivity deadline. There is no frame-start deadline. A client can occupy a slot by dripping bytes below five seconds, including before source validation. Independent test `review_j360_partial_frame_has_an_absolute_read_deadline` opens 64 connections, advertises 4,096-byte frames, and drips one byte/second for seven seconds. The original connection remains open (read returns WouldBlock), and the 65th connection is closed (EOF). All 64 slots remain unavailable to real clients. Add a frame/header completion deadline independent of incremental activity and cover slot recovery under drip traffic. Evidence: `repro.log`, repeated after formatting in `final-repro.log`; cargo exit 101. Finding first posted in 86254. 2. **P2: one continuously readable invalid TCP stream starves the shared UDP worker.** `dns.rs:231-285` loops over reads and restarts parsing after malformed/out-of-zone frames without a byte/frame budget. With a continuously writing client, it need not reach WouldBlock or return to poll. That also prevents the outer loop from checking shutdown and client deadlines. Independent test `review_j360_invalid_tcp_stream_yields_to_udp` sends batches of length-prefixed invalid 12-byte packets from a nonlocal source. A valid self-source UDP query times out during the flood; after stopping the writer, that exact queued query receives its 49-byte answer. Repeated twice, including the final formatted tests. Add a per-turn read/parse budget covering error and out-of-zone results, then return to the outer worker. Evidence: `stress.log`, `final-repro.log`; cargo exit 101. Finding first posted in 86259. This is a service-liveness reproducer, not a functional-test latency benchmark. Final independent regression result: ```text test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 8 filtered out; finished in 9.45s J360_FINAL_REPRO_EXIT=101 ``` ## J354 repairs and focused validation All build/test work ran on dev-02 through its build-slot, after reading the launcher and checking `nix develop . -c cargo --version` (cargo 1.99.0). Used CARGO_INCREMENTAL=0 and one target directory. Specific suites, serial socket tests: - `--lib review_j`: 3 passed. With `NMCLI_TEST_BIN` explicitly selecting the extracted real nmcli 1.46 binary and its libnm library, all 3 passed again without the parser skip. - `--lib ordinary_identity_reply`: 1 passed. - `--test access_updates`: 5 passed, 3 privileged ignored. - `--test configuration`: 25 passed. - `--test dns`: 13 passed, 1 privileged ignored. - Original `--test review_j354_dns`: 8 passed, including all J354 original DNS regressions and J254's TCP additions. The imported identity and nmcli tests retain J354's assertions; their changes are import/lint compatibility and added DNS/real-parser checks. Raw receipts: `focused.log`, `repro.log`. Privileged tests were not rerun in this review; J254's live proof is #266 comment 86238. **Capabilities:** `read_identity` preserves both pre-DNS probes byte-for-byte, then calls `read_dns_names` independently. The strict old-node fixture now reports rotation=Some(true), compact=true, DNS=false. J254's live kernel-engine proof separately reports both pre-DNS capabilities true and successful attachment/ping. In the opposite direction, `IdentityProbe` accepts the legacy requests, `identity_response` emits DNS only when requested, false extension fields are omitted, and Config/Chunk DNS fields have serde defaults. Thus a new node's reply does not add unknown DNS fields to an old server's strict decoder. The legacy reply-shape test passes. I did not deploy an old server. **nmcli:** production emits one comma-separated `ipv4.dns-search` value. It currently emits no `ipv6.dns-search` property. The real offline parser accepts the generated command. A separate actual nmcli invocation supplied the same comma-separated list to BOTH IPv4 and IPv6 properties and exited 0, rendering both correctly. Receipt: `nmcli-both-families.log`. No live connection was modified. **TCP bounds/privacy:** 64 active clients globally across listeners; 4,096-byte query cap (stricter than the u16 framing ceiling of 65,535); one bounded input/output frame; nonblocking reads/writes; five-second inactivity timeout. Existing cap/oversize/idle tests pass. Those bounds do not repair the two findings above. TCP and UDP call the same `response_for_source`: nonlocal sources receive NXDOMAIN/SOA before any name lookup, exactly as UDP; they are not rejected at accept time. Local policy-denied names are omitted from the same name map used for nonexistent names. There is no separate denied-name branch or lookup for nonlocal callers, so TCP adds no name-existence-dependent timing path. Wire equality passes; no constant-time guarantee or independent TCP timing distribution is claimed. J254's live TCP equality proof is in 86238. Loopback tests do not independently prove host firewall admission. **Fuzz:** target repairs compile. Independent nightly ASan cargo-fuzz run on the reviewed production source completed `Done 910117 runs in 31 second(s)`, with no artifacts (peak reported RSS 103 MB), max input length 65,535. Receipt: `fuzz.log`. Also read and retained J254's successful `Done 5507803 runs in 181 second(s)` receipt as `author-fuzz-180s.log`. This target exercises the parser/response boundary, not the TCP state machine; the new socket stress tests cover the latter. ## UDP performance claim Independently reran the supplied static before/after binaries on perf-01 through `/usr/local/bin/perf-run`, reversing initial order and interleaving three runs of each. Each reports 8 clients, 80,000 queries and 1,000 names; all exit 0. Retained stdout AND before/after machine-state receipts under `perf/`. | Run | UDP-only p50/p99, us | TCP-enabled p50/p99, us | | --- | --- | --- | | 1 | 152.906 / 325.129 | 55.063 / 73.929 | | 2 | 143.379 / 271.460 | 80.291 / 105.728 | | 3 | 142.918 / 225.302 | 51.426 / 129.642 | Median per-run p50/p99: 143.379/271.460 versus 55.063/105.728 us. The reported improvement is repeatable for these binaries, not merely a lucky original sample or an artefact of taking four-run medians. The saved dns.rs source hashes match ec71bc7ae and the reviewed head, and the retained author build script uses the same lockfile. However, the temporary benchmark example source was removed by that script, so I could not audit its timing boundaries or independently rebuild it. This is a limit on attributing the gain to TCP implementation work: the three-CPU, eight-client local round-trip workload includes scheduling and queueing, not isolated DNS processing or real-mesh latency. The author's named original perf directories contain machine snapshots but no query-output log; my rerun retains that output. No general production speedup is certified. More importantly, unloaded UDP numbers do not cover finding 2's demonstrated UDP outage under TCP input. ## Author full gate (not independently rerun) Per the common reviewer brief, no quick/fast rerun. Read the raw integrated-head author receipt from compute01 and retained it as `author-verify-fast-integrated.log`. Author exit 0 is recorded in #266 comment 85897. Table below quoted verbatim: ```text +---------------------------+--------------+ | Rust gate | Result | +---------------------------+--------------+ | Nix Cargo vendoring | PASS | | fmt | PASS | | migration names | PASS | | migration tooling tests | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | Traefik component manifest | PASS | | release component publication | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | doc coverage ratchet | PASS | | shape lints off (D351) | PASS | | doc comments on diff | PASS | | fixed test waits | PASS | | privileged integration | PASS | | probe release exclusion | PASS | | nightly fuzz reporter | PASS | | static builds | PASS | +---------------------------+--------------+ ``` ## Evidence and cleanup Attached archive contains raw reviewer logs, author gate/fuzz receipts, perf stdout and machine-state receipts, build script, and regression patch. Permanent control-host directory: `/home/kayg/.local/state/infraternal-orchestrator/j360/evidence/`. Reviewer logs also remain at `/home/claude/infraternal-ops/j360-evidence/` on dev-02; performance logs at the same path on perf-01. The single review target was deleted, own runners have exited, and copied benchmark executables were removed from perf-01. `cleanup.log` records the runner/target check. No live mesh claim, interface, resolver route, or server was changed. verdict: **REQUEST CHANGES**; reviewed head: `6f0a07af1daaaf657b716faa4bd51f05396c632e`; review branch head: `53c0441247f9095304be699eab2cdb0a747bc0a9`.
Author
Owner

J254 starting the J360 fixes on mesh-dns-j254 at 6f0a07af1d (merge base 6ffe37e067). I retrieved J360’s two reproducers and evidence: a byte-drip partial DNS/TCP frame holds connection slots past the inactivity timeout, and a continuous stream of invalid TCP frames prevents the shared worker from answering UDP. I am bringing both tests into this branch and will run them red before changing production code. The fixes will add an absolute message deadline and per-source cap, separate UDP service from TCP, and bound TCP work with a malformed-frame limit. Then I will measure UDP p50/p99 during a hostile TCP stream and run verify quick on dev-02 and verify fast on compute01. No merge or deployment.

J254 starting the J360 fixes on `mesh-dns-j254` at 6f0a07af1daaaf657b716faa4bd51f05396c632e (merge base 6ffe37e067d8fa7b090ea83c21b84b77937f76d9). I retrieved J360’s two reproducers and evidence: a byte-drip partial DNS/TCP frame holds connection slots past the inactivity timeout, and a continuous stream of invalid TCP frames prevents the shared worker from answering UDP. I am bringing both tests into this branch and will run them red before changing production code. The fixes will add an absolute message deadline and per-source cap, separate UDP service from TCP, and bound TCP work with a malformed-frame limit. Then I will measure UDP p50/p99 during a hostile TCP stream and run verify quick on dev-02 and verify fast on compute01. No merge or deployment.
Author
Owner

J360 TCP regressions reproduced red-first on dev-02 at 6f0a07af1 (no production changes): the malformed-stream test timed out on UDP while the TCP flood was active, and the byte-drip test kept its partial frame open after seven seconds. I’m splitting UDP/TCP workers, adding a two-second whole-frame deadline and a per-source cap, and bounding malformed TCP work. Evidence log: /home/claude/infraternal-ops/j254-evidence/j360-red.log.

J360 TCP regressions reproduced red-first on dev-02 at 6f0a07af1 (no production changes): the malformed-stream test timed out on UDP while the TCP flood was active, and the byte-drip test kept its partial frame open after seven seconds. I’m splitting UDP/TCP workers, adding a two-second whole-frame deadline and a per-source cap, and bounding malformed TCP work. Evidence log: /home/claude/infraternal-ops/j254-evidence/j360-red.log.
Author
Owner

Expanded red run on dev-02 (6f0a07af1, production unchanged): all four J360 tests fail as intended. UDP receives WouldBlock during the malformed TCP flood; the dripped frame stays open past 3 s; repeated malformed frames do not close the connection; and the ninth connection from one source stays open. Full log: /home/claude/infraternal-ops/j254-evidence/j360-review-red.log.

Expanded red run on dev-02 (6f0a07af1, production unchanged): all four J360 tests fail as intended. UDP receives WouldBlock during the malformed TCP flood; the dripped frame stays open past 3 s; repeated malformed frames do not close the connection; and the ninth connection from one source stays open. Full log: /home/claude/infraternal-ops/j254-evidence/j360-review-red.log.
Author
Owner

Pushed the J360 TCP fixes and current-dev base sync on mesh-dns-j254 at 5b41f20cf. UDP and TCP now run in separate workers; TCP has a 2 s whole-frame deadline, an eight-connection per-source cap, one bounded read per ready client per poll, and a 16-malformed-frame close limit. Focused DNS suite on dev-02: 12 passed. Next: verify quick on dev-02, verify fast on compute01, then measure UDP p50/p99 during continuous hostile TCP traffic on perf-01.

Pushed the J360 TCP fixes and current-dev base sync on mesh-dns-j254 at 5b41f20cf. UDP and TCP now run in separate workers; TCP has a 2 s whole-frame deadline, an eight-connection per-source cap, one bounded read per ready client per poll, and a 16-malformed-frame close limit. Focused DNS suite on dev-02: 12 passed. Next: verify quick on dev-02, verify fast on compute01, then measure UDP p50/p99 during continuous hostile TCP traffic on perf-01.
Author
Owner

J254 update — branch mesh-dns-j254, head 5b41f20cfad9766fc109a1f23e5cf798073b4316 (merge base f8fe487febea). J360's two regression reproducers were added red-first; the focused DNS review suite passes 12/12, and the privileged row has passed all 100 rows so far. The current verify fast run on compute01 is still active, finishing its aarch64 static build. Clippy reported six needless_pass_by_value findings on the UDP/TCP worker arguments; I have a narrow borrow-signature fix prepared and will apply it after this run exits, then rerun the full gate. The active worktree has not been edited during the run.

J254 update — branch `mesh-dns-j254`, head `5b41f20cfad9766fc109a1f23e5cf798073b4316` (merge base `f8fe487febea`). J360's two regression reproducers were added red-first; the focused DNS review suite passes 12/12, and the privileged row has passed all 100 rows so far. The current `verify fast` run on compute01 is still active, finishing its aarch64 static build. Clippy reported six `needless_pass_by_value` findings on the UDP/TCP worker arguments; I have a narrow borrow-signature fix prepared and will apply it after this run exits, then rerun the full gate. The active worktree has not been edited during the run.
Author
Owner

J360 TCP fairness fix round started on mesh-dns-j254 at 5b41f20cfad9766fc109a1f23e5cf798073b4316 (review findings against 6f0a07af1).

Red-first evidence: j360-red-all.log reproduces the partial-frame deadline and malformed TCP flood starving UDP; j360-review-red.log reproduces all four targeted cases failing before the fix. The post-fix focused run in j360-green-focused.log passes all 12 DNS review tests. The listener now enforces a whole-message deadline and per-source cap, bounds TCP work and malformed frames, keeps UDP on its own worker, and yields between TCP poll batches.

perf-run UDP query latency under hostile TCP load, p50/p99 in microseconds:

  • One hostile TCP client: baseline 68.6/83.1; under load 67.0/115.1.
  • Eight hostile TCP clients: baseline 68.6/82.2; under load 73.6/343.4. The eight-client p99 rises under this stress and remains variable.

verify quick on dev-02 exited 0. The final post-yield verify fast is still running on compute01; I will post its exact result and the pushed head when it finishes.

J360 TCP fairness fix round started on `mesh-dns-j254` at `5b41f20cfad9766fc109a1f23e5cf798073b4316` (review findings against `6f0a07af1`). Red-first evidence: `j360-red-all.log` reproduces the partial-frame deadline and malformed TCP flood starving UDP; `j360-review-red.log` reproduces all four targeted cases failing before the fix. The post-fix focused run in `j360-green-focused.log` passes all 12 DNS review tests. The listener now enforces a whole-message deadline and per-source cap, bounds TCP work and malformed frames, keeps UDP on its own worker, and yields between TCP poll batches. `perf-run` UDP query latency under hostile TCP load, p50/p99 in microseconds: - One hostile TCP client: baseline 68.6/83.1; under load 67.0/115.1. - Eight hostile TCP clients: baseline 68.6/82.2; under load 73.6/343.4. The eight-client p99 rises under this stress and remains variable. `verify quick` on dev-02 exited 0. The final post-yield `verify fast` is still running on compute01; I will post its exact result and the pushed head when it finishes.
Author
Owner

J254's J360 TCP fixes are complete and pushed on mesh-dns-j254 at 787c4473058d81724cb7d8fa6dd93abe9ca67d94. No merge.

The reproducers were run red-first: j360-red-all.log shows the incomplete-frame and malformed-stream starvation failures; j360-review-red.log shows all four TCP cases failing before the fix. j360-green-focused.log records 12 passed, 0 failed. TCP now has a whole-message deadline, per-source connection cap, bounded reads and malformed-frame limit. UDP has an independent worker; TCP yields after bounded poll batches.

perf-run UDP latency under hostile TCP load (p50/p99, µs):

  • One hostile TCP client: baseline 68.6/83.1; under load 67.0/115.1.
  • Eight hostile TCP clients: baseline 68.6/82.2; under load 73.6/343.4. The eight-client p99 remains elevated under stress.

verify quick on dev-02 exited 0. Its summary:
| Rust gate | Result |
+---------------------------+--------------+
| scope guard | PASS |
| migration names | PASS |
| Nix Cargo vendoring | PASS |
| fmt | PASS |
| doc coverage ratchet | PASS |
| shape lints off (D351) | PASS |
| doc comments on diff | PASS |
| fixed test waits | PASS |
| clippy (workspace) | PASS |
| unit tests (changed) | PASS |
| unit tests (changed, bin-only) | PASS |
+---------------------------+--------------+

verify fast on compute01 exited 0. Its summary:
| Rust gate | Result |
+---------------------------+--------------+
| Nix Cargo vendoring | PASS |
| fmt | PASS |
| migration names | PASS |
| migration tooling tests | PASS |
| VT native fmt | PASS |
| CLI plugin compile-out | PASS |
| clippy | PASS |
| SSH runtime test helper | PASS |
| real-binary fixture paths | PASS |
| Traefik component manifest | PASS |
| release component publication | PASS |
| test | PASS |
| SSH inode reuse fixture | PASS |
| shim process-per-user | PASS |
| cargo-deny | PASS |
| gate architecture | PASS |
| doc coverage ratchet | PASS |
| shape lints off (D351) | PASS |
| doc comments on diff | PASS |
| fixed test waits | PASS |
| privileged integration | PASS |
| probe release exclusion | PASS |
| nightly fuzz reporter | PASS |
| static builds | PASS |
+---------------------------+--------------+

Final protocol checks:
50 pass
0 fail
77253 expect() calls
Ran 50 tests across 13 files. [6.07s]

J254's J360 TCP fixes are complete and pushed on `mesh-dns-j254` at `787c4473058d81724cb7d8fa6dd93abe9ca67d94`. No merge. The reproducers were run red-first: `j360-red-all.log` shows the incomplete-frame and malformed-stream starvation failures; `j360-review-red.log` shows all four TCP cases failing before the fix. `j360-green-focused.log` records 12 passed, 0 failed. TCP now has a whole-message deadline, per-source connection cap, bounded reads and malformed-frame limit. UDP has an independent worker; TCP yields after bounded poll batches. `perf-run` UDP latency under hostile TCP load (p50/p99, µs): - One hostile TCP client: baseline 68.6/83.1; under load 67.0/115.1. - Eight hostile TCP clients: baseline 68.6/82.2; under load 73.6/343.4. The eight-client p99 remains elevated under stress. `verify quick` on dev-02 exited 0. Its summary: | Rust gate | Result | +---------------------------+--------------+ | scope guard | PASS | | migration names | PASS | | Nix Cargo vendoring | PASS | | fmt | PASS | | doc coverage ratchet | PASS | | shape lints off (D351) | PASS | | doc comments on diff | PASS | | fixed test waits | PASS | | clippy (workspace) | PASS | | unit tests (changed) | PASS | | unit tests (changed, bin-only) | PASS | +---------------------------+--------------+ `verify fast` on compute01 exited 0. Its summary: | Rust gate | Result | +---------------------------+--------------+ | Nix Cargo vendoring | PASS | | fmt | PASS | | migration names | PASS | | migration tooling tests | PASS | | VT native fmt | PASS | | CLI plugin compile-out | PASS | | clippy | PASS | | SSH runtime test helper | PASS | | real-binary fixture paths | PASS | | Traefik component manifest | PASS | | release component publication | PASS | | test | PASS | | SSH inode reuse fixture | PASS | | shim process-per-user | PASS | | cargo-deny | PASS | | gate architecture | PASS | | doc coverage ratchet | PASS | | shape lints off (D351) | PASS | | doc comments on diff | PASS | | fixed test waits | PASS | | privileged integration | PASS | | probe release exclusion | PASS | | nightly fuzz reporter | PASS | | static builds | PASS | +---------------------------+--------------+ Final protocol checks: 50 pass 0 fail 77253 expect() calls Ran 50 tests across 13 files. [6.07s]
Sign in to join this conversation.
No labels
launch-qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/infraternal#266
No description provided.