Status: DRAFT, prespecified 2026-09-30 by the Claude session on the Mac, at Travis's go. Not reviewed by GPT. Nothing is built or run. On review: freeze it, then build in the order in §5. This is GPT's execution-order item 9 (R0.9-OPEN §8a). lab/scripts/nc35_layer_substitution.sh says it itself: "The scripted transport is NOT the claim -- it is the harness … The kernel-WireGuard execution of A and B is a separate leg." This file is that leg's contract, written before its code.
The cases keep nc35's letters and nc35's expected values (nc35_layer_substitution.sh lines 145–156).
| Case | Setup | Kernel evidence required | Connector result required |
|---|---|---|---|
| A | Right PeerExpectation (N1, K1). The endpoint is held by a DIFFERENT WireGuard peer (K2). | K1's latest handshake stays 0 for the whole bound T | TransportNotAuthenticated, and no session |
| B | Right transport peer (K1). Its responder answers with NodeId N2 ≠ N1. | K1's latest handshake ADVANCES after the probe starts, within T | NodeIdMismatch (ConnectError::NodeIdMismatch), and no session |
| E (positive control) | Everything correct | K1's handshake advances | session: a ValidatedPeerSession with N1, K1 and the probed address |
| U (control) | The endpoint's interface is down, or no route | none | TransportUnreachable, not TransportNotAuthenticated |
B passes only with BOTH halves. The kernel must show K1 authenticated, AND the connector must refuse at the
NodeId comparison. A B that fails at transport proved the transport check twice and the semantic check never (nc35's header).
E and U exist so A and B cannot pass vacuously. A leg where nothing ever authenticates would "pass" A and fail
E. A leg that folds "down" into "unauthenticated" would pass A and fail U.
Not claimed: key rotation (R0.9 does not own it, §7a), paths between separate hosts, relays, mobility, or
anything the scripted nc35 already covers (C, D, P, F stay scripted).
In the existing R0.x lab on the PX13, exactly where R0.6–R0.8's WireGuard legs ran. Verified read-only on 2026-09-30:
The privileged Docker runner is ~/computedriven-r05-qualification/runner/run-r09.sh, "the same privilege model as
the accepted R0.5-R0.8 runner mechanism". The user travis is in the docker group, so no sudo is needed.
containerlab topologies from lab/clab-cd-r0x/.
The node image is lab/Dockerfile.cd-node-wg (Ubuntu, wireguard-tools, the static cd-node).
Kernel WireGuard interfaces come from the tree's own lab/scripts/wg_overlay*.sh (ip link add … type wireguard).
It needs no locuchest, and no Super host.script.run (F1). cd-floor-01 stays bench-only (Travis 2026-09-30), and the leg never runs there. Any battery-grade timing belongs under docs/battery-host-profile.md; this leg makes no timing claim.
`KernelWgPeer`: an implementation of cd-connect's TransportPeer trait (cd-connect/src/lib.rs), living in
lab tooling. It never goes in a frozen crate and never in cd-rendezvous: the boundary guard "cd-connect has exactly one non-test call site" must still hold. It reads the kernel's view with wg show <if> dump (or netlink) and compares decoded 32-byte keys, never strings. R0.6's gate compared 64 hex characters against 44 base64 characters and could never fire.
The outcome, derived from kernel evidence only:
Authenticated if the expected peer's latest handshake advances after the probe starts, within T;
NotAuthenticated if the interface is up and the endpoint is routable, but no handshake completes within T;
Unreachable if the interface is down or there is no route.
T is fixed in advance at 10 s. WireGuard's REKEY_TIMEOUT is 5 s, so one retry fits.
The responder is the unmodified frozen cd-node HELLO/ACK in the endpoint's container, configured with N1 (for
E) or N2 (for B). The frozen crates are not touched (R0.8-FROZEN, tools/verify_r08_freeze.sh).
Keys: generated per run with wg genkey inside the containers, loaded with wg set … private-key, and never
written to the host or to any receipt. Receipts carry only public keys' SHA-256 prefixes. The local private key stays in the kernel (§7a).
A, B, E and U each produce their required kernel evidence AND connector result (§1). Anything else fails.
The scripted nc35_layer_substitution.sh still passes, unchanged.
tools/guards.sh is ALL GUARDS PASS, including the five R0.9 boundary assertions.
tools/verify_r08_freeze.sh is unchanged.
Teardown leaves no lab containers and no WireGuard interfaces, and the run records the host's module state before
and after.
Falsification commit: lab/scripts/nc35k_kernel_leg.sh exists and is wired as battery leg 18 of run-r09.sh,
with the four-places check (rc_ init, run_leg, FINAL, jq args; §7 H-family). It FAILS with "no kernel transport — the kernel leg has no subject yet".
Mechanism: KernelWgPeer and the leg's topology, until §4 holds.
Mutants, each caught by exactly its case, or with the overlap declared:
(m1) KernelWgPeer reports Authenticated without reading the kernel → A goes red;
(m2) keys compared in two different encodings → E and B go red. This overlap is declared: both need the
expected peer to match, and a key that never matches makes neither authenticate;
(m3) the responder's NodeId is not compared → B goes red at the semantic step;
(m4) Unreachable is folded into NotAuthenticated → U goes red.
Execution under run-r09.sh, with a receipt receipts/R0.9-NC35K-EXECUTED.md: the raw wg show … dump
lines (public-key prefixes only), every exit code, and the tree's HEAD.
Then R0.9-OPEN §9's closure list, and GPT's freeze review.
T = 10 s: accepted?
R97 says every stage is built WITH Super. This leg runs in the lab runner, which Super cannot drive today:
repository-python-gate@1's table for this repository names only tools/succession_laws_gate.py. Accept the lab runner for this leg, or route the code change through a Super review first?
The root-owned `target/release` (since 2026-09-03) makes the guards refuse the default build directory. Fix
its ownership (that needs root), or keep using CARGO_TARGET_DIR?