The Windows client already prepared TAP before relay resolution, but it still
opened the QUIC control connection before installing the relay host route. TAP
was not active yet, so this was not the self-routing failure mode, but it left
the relay flow unpinned during the control handshake.
Resolve the relay, pin and verify the relay host route, then open the relay
connection. Keep the after-TAP verification as the guard against DHCP or TAP
route policy taking over the relay path. Update README.md and TESTING.md so the
MVP startup description and expected log order match that safer flow.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check -p lanparty-client-route --target x86_64-pc-windows-msvc
- git diff --check
- git diff --cached --check
Windows-target check attempted:
- cargo check -p lanparty-client-win --target x86_64-pc-windows-msvc
With LLVM tools configured, that still stops inside ring on this Linux host
because the Windows C headers are unavailable, starting with assert.h.
Refs: MVP relay-route protection
The Windows client pinned the relay host route before opening TAP, but only
verified that Windows was actually using that host route after TAP activation.
If an existing route or pinning failure prevented the host route from becoming
the active relay path, the client could discover that only after touching TAP
route policy.
Verify the pinned host route immediately after installing/reusing it, before
opening the TAP adapter. Keep the existing after-activation verification as the
runtime guard against TAP route takeover, and document the new expected startup
log line in the MVP guide.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check -p lanparty-client-route --target x86_64-pc-windows-msvc
- git diff --check
- git diff --cached --check
Windows-target check attempted:
- cargo check -p lanparty-client-win --target x86_64-pc-windows-msvc
With LLVM tools configured, that still stops inside ring on this Linux host
because the Windows C headers are unavailable, starting with assert.h.
Refs: MVP relay-route protection
Windows MVP debugging needs more than aggregate drop counters when LAN traffic
reaches the client but is kept out of the TAP adapter. A DHCP or discovery
failure is much easier to diagnose when the client log says which relayed frame
was filtered and why.
Expose a client receive outcome that preserves the existing accepted-frame API
while allowing the Windows frame pump to log filtered RelayToTap frames with
the source peer and drop reason. Document the new log signal in the README and
manual MVP test guide.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-core connects_to_relay_control_stream_as_client
- cargo test -p lanparty-client-win formats_client_frame_log_lines
- cargo test -p lanparty-client-core
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy -p lanparty-client-core --all-targets -- -D warnings
- cargo clippy -p lanparty-client-win --all-targets -- -D warnings
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Windows-target check attempted:
- cargo check -p lanparty-client-win --target x86_64-pc-windows-msvc
The Windows-target check is still blocked on this Linux host before compiling
lanparty-client-win because ring cannot find the MSVC lib.exe tool.
Refs: MVP client diagnostics
The relay welcome now carries the gateway peer id, but the first Windows client
startup line still only showed whether a gateway was connected. Surface the peer
id there when it is known so manual MVP testers can record it even before or
without relying on a later lifecycle catch-up line.
Keep the older yes/no text when a gateway is connected but the peer id is not
available, which preserves useful output for older welcome payloads.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win gateway_status
- cargo clippy -p lanparty-client-win --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP manual validation
Refresh the manual MVP test guide around the current relay, gateway, and
Windows client behavior. The guide now calls out the values to record during a
run, the exact process order, expected gateway lifecycle signals, and a short
gateway restart sanity check.
Also update the README component notes so the control plane and Windows client
docs mention the gateway peer id carried in welcomes.
Test Plan:
- git diff --check
- git diff --cached --check
Refs: MVP manual validation
The MVP tunnel negotiates an effective TAP MTU and configures the Windows TAP IP
interface to that value, but the forwarding path only rejected frames that were
standard-Ethernet jumbo frames or exceeded the QUIC datagram budget. A frame
could therefore be larger than the negotiated TAP MTU while still fitting inside
the QUIC datagram budget.
Make the TAP-MTU frame limit an explicit shared protocol helper and enforce it
at every data-path boundary: Windows client send/receive, Linux gateway
send/receive, and relay forwarding. Such frames now produce TapMtuExceeded in
logs and counters instead of being forwarded until a later layer drops or
accepts them implicitly.
This keeps the no-fragmentation contract honest: one Ethernet frame still maps
to one QUIC datagram, but only if that frame also fits the room's negotiated TAP
MTU.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-proto tap_mtu
- cargo test -p lanparty-client-core connects_to_relay_control_stream_as_client
- cargo test -p lanparty-gateway connects_to_relay_control_stream_as_gateway
- cargo test -p lanparty-relay drops_frames_above_effective_tap_mtu
- cargo test -p lanparty-relay rate_limits_client_total_bandwidth_after_burst
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo build --release -p lanparty-relay -p lanparty-gateway
- git diff --check
- git diff --cached --check
Refs: MVP no-fragmentation tunnel MTU contract
The client previously reported "Broadcast traffic flowing" as soon as either
broadcast TX or RX was nonzero. During the MVP DHCP/ARP proof, one-way
broadcast is useful but weaker evidence than bidirectional broadcast.
Keep the existing healthy message for two-way broadcast, but report
outbound-only broadcast as a warning that the client is still waiting for a LAN
broadcast reply, and report inbound-only broadcast separately. This makes the
Windows client status lines more precise when DHCP is stuck.
Test Plan:
- cargo test -p lanparty-obs
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP Windows DHCP diagnostics
The relay and gateway already emit structured frame logs, but the Windows
client only exposed aggregate counters. During the MVP end-to-end test that
left a blind spot between TAP reads/writes and the relay datagram path.
Add client-side frame log lines for accepted TAP-to-relay sends,
relay-to-TAP writes, and local TAP-frame drops before relay send. The logs use
the shared FrameLog vocabulary with TapToRelay and RelayToTap directions so the
client, relay, and gateway logs can be correlated during DHCP, ARP, ping, and
LAN-game discovery checks.
Test Plan:
- cargo test -p lanparty-client-win formats_client_frame_log_lines
- cargo test -p lanparty-client-win
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP Windows client diagnostics
The MVP test guide asked for a Windows release build but did not make the MSVC
native-toolchain dependency explicit. The client dependency stack includes
native code, so a fresh Windows test machine needs the Rust MSVC host and C++
Build Tools available before cargo can build lanparty-client-win.
Add a short prerequisite section with quick checks for rustc host, cl.exe, and
lib.exe. This gives the manual Windows tester a faster explanation if the build
environment is missing Visual Studio Build Tools.
Test Plan:
- cargo test -p lanparty-client-win --help >/dev/null
- git diff --check
- git diff --cached --check
Refs: MVP Windows test handoff
The gateway already sent periodic CAM refresh frames so the physical switch
keeps remote client MACs learned on the gateway port. Those periodic refreshes
were silent, which made the manual MVP switch-learning check harder to diagnose
after the initial peer-joined refresh had passed.
Keep the peer id and MAC attached to periodic refresh work and emit the same
structured CAM refresh log line with reason=periodic. The join-triggered
refresh still logs reason=peer_joined, so the test guide can distinguish the
immediate proof from the recurring keepalive signal.
Test Plan:
- cargo test -p lanparty-gateway cam_refresh
- cargo test -p lanparty-gateway
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: MVP switch MAC-learning diagnostics
Use the canonical --interface flag in the README and MVP test guide so the
handoff command matches lanparty-gateway --help. Keep --iface documented as the
shorter accepted alias because the CLI and tests still support it.
Test Plan:
- ./target/release/lanparty-gateway --help
- cargo test -p lanparty-gateway accepts_iface_alias_for_gateway_interface
- git diff --check
- git diff --cached --check
Refs: MVP test handoff polish
Add the concrete pass conditions for the manual MVP proof and a small fill-in
block for relay, room, gateway interface, and LAN host values. This makes the
operator flow easier to follow before starting the relay, gateway, and Windows
client.
The guide also calls out the gateway lifecycle and CAM-refresh log lines that
matter for switch MAC learning, plus the non-link-local TAP IPv4 address to use
for ping tests.
Test Plan:
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
- git diff --cached --check
Refs: PLAN.md MVP manual end-to-end proof
Windows can report more than one unicast address on the TAP adapter. If an
APIPA address remains present next to the real LAN DHCP address, choosing the
first IPv4 address can make diagnostics warn about 169.254.x.x even though the
adapter already has a usable LAN address.
Prefer a non-link-local IPv4 address for TAP diagnostics, then fall back to any
IPv4 address, then to the first non-IPv4 address. This keeps the existing APIPA
warning when APIPA is the only IPv4 signal, but reports the real DHCP address
when Windows exposes both addresses.
README.md and TESTING.md now document that diagnostics prefer the real LAN IPv4
when several TAP addresses are visible.
Test Plan:
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
Refs: PLAN.md client diagnostics; TESTING.md TAP IP verification
The gateway runs in promiscuous mode so it can capture frames for remote client
MACs, but that also means it sees unrelated unicast traffic between physical
LAN machines. Before this change those frames were sent to the public relay and
only then discarded as unknown destinations.
Use the remote-client table seeded by relay lifecycle events to decide whether
a LAN frame should leave the gateway. Broadcast and multicast traffic still
flows to the relay, and unicast to a connected remote client still flows to the
relay. Unicast to any other destination is counted and logged locally as
UnknownDestination.
This keeps busy LAN traffic out of the relay data path and makes the gateway
behavior match the MVP switching model: LAN frames go to matching remote
clients, while broadcast and multicast fan out.
README.md documents the local filter, and TESTING.md explains why
LanToRemote UnknownDestination can be normal on a busy LAN.
Test Plan:
- cargo test -p lanparty-gateway
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
Refs: PLAN.md switching model; TESTING.md MVP log signals
The gateway already emits periodic CAM refresh frames so the physical switch
keeps remote client MACs on the gateway port. A newly joined client previously
waited until either its own tunneled traffic reached the LAN or the first
60-second refresh tick fired.
Emit one padded CAM refresh frame immediately when a valid client PeerJoined
control event is observed. This makes the switch MAC-table check in the MVP
procedure visible sooner and keeps the periodic refresh as the aging guard.
The refresh uses the same maintenance frame shape as the periodic path and is
logged with the client peer id and MAC.
README.md and TESTING.md now document the immediate refresh behavior and the
log signal to look for during manual LAN testing.
Test Plan:
- cargo test -p lanparty-gateway
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
Refs: PLAN.md CAM refresh; TESTING.md MVP switch MAC-table check
IPv4 link-local on the TAP adapter means Windows self-assigned APIPA because
LAN DHCP did not complete through the tunnel. The previous user diagnostic
only said "TAP IP detected", which made a failed DHCP path look neutral
during MVP testing.
Return a full UserDiagnostic from TAP IP classification so IPv4 link-local
can be a warning while normal IPv4 still reports the received DHCP address.
Keep IPv6 link-local neutral because it is expected on many Windows
interfaces and is not evidence of LAN DHCP success or failure.
TESTING.md now tells the operator to troubleshoot 169.254.x.x like
`Waiting for TAP IP`.
Test Plan:
- cargo test -p lanparty-obs
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
Refs: PLAN.md diagnostics; TESTING.md MVP guide
The Windows startup path opened the TAP adapter to clear stale media state
before loading the persisted tunnel identity and writing the NetworkAddress
registry value. That made the first TAP touch happen with whatever MAC Windows
already had loaded from a previous run.
Build the client runtime config first so the virtual MAC and resolved relay
address are known before TAP activation. The pre-connect TAP preparation now
writes the selected adapter's NetworkAddress value before opening the adapter to
mark media disconnected. The relay route is still pinned only after the relay
handshake, while TAP remains media-disconnected.
This reduces the chance of a first-run or post-crash TAP MAC mismatch during
the manual Windows MVP test. The client still validates the driver-reported MAC
before bridging and still fails closed if Windows has not reloaded the registry
value.
Test Plan:
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
- cargo check -p lanparty-client-win --target x86_64-pc-windows-gnu
- blocked by missing x86_64-w64-mingw32-gcc for ring on this host
Refs: PLAN.md Windows routing / metric handling; TESTING.md MVP guide
The relay already enforces client source MAC identity before forwarding, but
this gateway bridge could still write any safety-clean relayed frame to
AF_PACKET. That left the final physical-LAN boundary depending entirely on the
relay forwarding path.
Keep a lifecycle-seeded remote client table in the gateway bridge and reject
relay frames whose datagram peer id is unknown or whose Ethernet source MAC does
not match the announced client MAC. CAM refresh now uses the same announced
table instead of learning source MACs from relay traffic.
This is conservative: if data arrives before the lifecycle event, the gateway
drops that frame with UnauthorizedSourceMac. Later packets proceed after the
control event is processed.
Test Plan:
- cargo test -p lanparty-gateway connects_to_relay_control_stream_as_gateway
- cargo test -p lanparty-gateway
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo fmt --check
- git diff --check
Refs: PLAN.md safety filters; TESTING.md MVP acceptance guide
Safety filtering now applies at several tunnel boundaries. The relay remains
the trust boundary, while the client and gateway also drop unsafe frames before
spending relay bandwidth. Duplicating EtherType and IPv4/IPv6 parsers across
crates would make those rules drift as the MVP grows.
Move the Ethernet safety classifiers into lanparty-proto, expose typed safety
drop reasons, and map them back into the existing DropReason vocabulary. The
relay now uses the shared client and gateway classifiers, the gateway keeps its
local LAN-send drops through the shared classifier, and the client drops the
same remote-to-LAN safety cases before QUIC DATAGRAM encoding.
Document the client-side local drops and list the additional suspicious drop
reasons in the manual MVP test guide.
Test Plan:
- cargo test -p lanparty-proto safety
- cargo test -p lanparty-client-core connects_to_relay_control_stream_as_client
- cargo test -p lanparty-gateway connects_to_relay_control_stream_as_gateway
- cargo test -p lanparty-relay
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check -p lanparty-client-tap --target x86_64-pc-windows-gnu --tests
- cargo check -p lanparty-client-route --target x86_64-pc-windows-gnu --tests
- cargo check -p lanparty-client-tap --target x86_64-pc-windows-msvc --tests
- cargo check -p lanparty-client-route --target x86_64-pc-windows-msvc --tests
- git diff --check
Refs: PLAN.md safety filters and client source-MAC isolation
The relay already rejects client frames whose source MAC does not match the
announced virtual MAC. The Windows bridge can still see those frames from TAP,
though, and sending them to the relay wastes datagram budget and makes the
client-side counters less useful during manual tests.
Carry the configured virtual MAC into ClientRelayIo and drop invalid or
unauthorized TAP source MACs before QUIC DATAGRAM encoding. The relay keeps the
same checks as the trust boundary, but client diagnostics now account for these
drops locally.
Document the local source-MAC check and list InvalidSourceMac as a suspicious
manual-test drop reason.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-core connects_to_relay_control_stream_as_client
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md source-MAC authorization and safety filters
A gateway with the selected Ethernet cable unplugged can still open an
AF_PACKET socket and join the relay room. That makes clients see a connected LAN
gateway even though DHCP and LAN discovery cannot make the physical round trip.
Check the selected Linux interface's sysfs carrier file before creating the raw
socket. If sysfs reports carrier 0, fail before the gateway joins the relay.
Missing or unrecognized carrier files remain allowed so this does not reject
interfaces where the kernel cannot expose link state in that form. README and
TESTING now document the preflight and the operator fix.
Test Plan:
All cargo commands used these environment variables:
RUSTUP_HOME=/tmp/softlan-vpn-rustup
CARGO_HOME=/tmp/softlan-vpn-cargo
- cargo test -p lanparty-gateway \
packet::tests::detects_disconnected_interfaces_from_sysfs_carrier
- cargo test -p lanparty-gateway
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md wired Ethernet gateway requirement
If the Windows client is killed hard, the TAP adapter can be left in a connected
media state. A retry should not resolve or connect to the relay while stale TAP
state might still influence Windows routing.
Select and open the intended TAP adapter before relay endpoint resolution, force
its media state to disconnected, then proceed with the existing relay connect,
route pin, TAP route protection, and bridge startup flow. This also makes
missing or ambiguous TAP adapters fail before the client joins the relay room.
The README and MVP test guide now show the new startup line and the early TAP
preflight troubleshooting checks.
Test Plan:
All cargo commands used these environment variables:
RUSTUP_HOME=/tmp/softlan-vpn-rustup
CARGO_HOME=/tmp/softlan-vpn-cargo
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check -p lanparty-client-tap --tests
--target x86_64-pc-windows-gnu
- cargo check -p lanparty-client-tap --tests
--target x86_64-pc-windows-msvc
- cargo check -p lanparty-client-route --tests
--target x86_64-pc-windows-gnu
- cargo check -p lanparty-client-route --tests
--target x86_64-pc-windows-msvc
- git diff --check
Known limitation: full lanparty-client-win Windows cross-check is still blocked
on this Linux host by the external ring toolchain setup. The default GNU target
lacks x86_64-w64-mingw32-gcc, the default MSVC target lacks lib.exe, and the
LLVM MSVC attempt gets as far as ring C compilation but lacks Windows CRT
headers such as assert.h.
Refs: PLAN.md route-protection startup requirement
A crashed or forcibly killed Windows client can leave the scoped relay host
route behind. The next run should still be allowed to start when Windows says
that the exact route row already exists, because that route already protects the
relay path from TAP default-route takeover.
Handle ERROR_OBJECT_ALREADY_EXISTS from CreateIpForwardEntry2 as a successful
borrowed pin. Routes created by the current client are still deleted on Drop;
pre-existing routes are left alone so we do not remove administrator-managed or
stale routes that this process did not create. The client startup log now marks
whether the route was created or already existed, and the README and MVP test
guide explain the behavior.
Test Plan:
All cargo commands used these environment variables:
RUSTUP_HOME=/tmp/softlan-vpn-rustup
CARGO_HOME=/tmp/softlan-vpn-cargo
- cargo fmt --check
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check -p lanparty-client-route --tests
--target x86_64-pc-windows-gnu
- cargo check -p lanparty-client-route --tests
--target x86_64-pc-windows-msvc
- cargo clippy -p lanparty-client-route --tests
--target x86_64-pc-windows-gnu -- -D warnings
- cargo clippy -p lanparty-client-route --tests
--target x86_64-pc-windows-msvc -- -D warnings
- git diff --check
Known limitation: full lanparty-client-win Windows cross-check is still blocked
on this Linux host by the external ring toolchain setup. The GNU target lacks
x86_64-w64-mingw32-gcc, and the MSVC target lacks lib.exe/MSVC environment.
Refs: PLAN.md route-protection requirement
The Windows client already treats TAP route, metric, and MTU changes as scoped
runtime state that is restored when the process exits. TAP media status was the
odd one out: startup marked the adapter connected, but there was no matching
cleanup path to mark it disconnected again.
Add a small TAP media guard that owns an Arc to the opened adapter and marks
media disconnected on drop. The frame pump now receives an Arc<TapAdapter>, so
the guard can run on normal Ctrl-C, startup errors after TAP open, route-check
failures, or frame-pump failures without fighting ownership of the TAP reader
thread. Cleanup errors are logged because Drop cannot return them.
Update the README and MVP test guide so the documented cleanup behavior matches
the client runtime.
The full Windows client cross-check is still blocked on this host before project
code by ring needing x86_64-w64-mingw32-gcc. The TAP and route helper crates
still check for the Windows GNU target.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win -p lanparty-client-tap -p lanparty-client-route
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- cargo check --target x86_64-pc-windows-gnu -p lanparty-client-route -p lanparty-client-tap
- git diff --check
Refs: MVP Windows TAP cleanup
The gateway previously joined the relay room before opening the Linux
AF_PACKET socket. If the operator passed the wrong interface, a wireless
interface, or an interface that could not be opened, the relay could briefly
advertise a gateway that was never able to bridge LAN traffic.
Parse CLI args first, then keep platform handling in a small cfg-gated run
function. On Linux, build the config, open the AF_PACKET socket, and only then
join the relay as the room gateway. On non-Linux targets, fail before reading
relay certificate files, resolving relay names, or opening network connections.
Update the README and MVP test guide so the documented gateway startup order
matches the binary output.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-gateway
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: MVP gateway startup hardening
Add a --list-tap-adapters mode to the Windows client. The command prints the
TAP-Windows6 adapter instance ids and exits without requiring relay, room, or
certificate arguments.
This makes the manual MVP test smoother on machines with multiple TAP adapters:
the operator can ask the binary for the exact ids, then rerun with
--tap-instance-id instead of relying on a separate PowerShell query or waiting
for the ambiguous-adapter startup error.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md Windows client TAP adapter responsibility
Add a Windows client --tap-instance-id option for selecting a specific
TAP-Windows6 adapter by NetCfgInstanceId / InterfaceGuid. The client still opens
the only installed TAP adapter automatically, but now refuses to choose an
arbitrary adapter when multiple TAP-Windows6 adapters are present.
This keeps the MVP test run from silently configuring and opening the wrong TAP
adapter on machines that already have VPN or test TAP devices installed. The
error lists available adapter instance ids so the operator can rerun with the
intended value.
Test Plan:
- cargo fmt --check
- cargo test -p lanparty-client-win
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check
Refs: PLAN.md Windows client TAP adapter responsibility
Add a dedicated manual test guide for the MVP proof. The guide keeps the
operator flow concrete: relay startup, certificate handoff, Linux gateway
startup, elevated Windows client startup, expected log signals, acceptance
checks, troubleshooting, cleanup, and the information to report back.
This is intentionally scoped to the manual MVP. It documents that installer,
GUI, production certificates, authentication, and end-to-end payload encryption
are outside this proof.
Test Plan:
- git diff --check
- reviewed TESTING.md for command flow and expected log signals
Refs: PLAN.md Phase 1 success criteria