From 93c6133ea949387136bc7776a098e47886e59337 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 16 May 2026 18:51:28 +0200 Subject: [PATCH] feat(peer-cli): run harness containers on a macvlan network The previous `peer-cli-run` recipe attached containers with `--network host`. That makes every peer share the host network namespace, which means two harness containers on the same machine cannot independently advertise mDNS or look like distinct LAN devices: they all reuse the host's interface, the mDNS daemon is single-instance per namespace, and any port the peer binds is a host-wide port. That defeats the whole point of running multiple peers side-by-side for end-to-end testing. Switch the recipe to a Docker macvlan network. Each container gets its own MAC and IP carved out of the real LAN subnet, sends and receives multicast on the parent NIC, and appears to the rest of the home network as a fresh device. mDNS discovery then works between peers exactly as it would for two laptops on the same LAN, with no relay, reflector, or special routing. Add a `peer-cli-net` recipe that creates the network idempotently (the `docker network inspect` short-circuits when it already exists), make `peer-cli-run` depend on it, and parameterise the parent interface, subnet, and gateway as justfile variables so they can be overridden from the command line for machines whose LAN does not match the defaults: just LANSPREAD_PARENT_IFACE=enp4s0 \ LANSPREAD_SUBNET=10.0.0.0/24 \ LANSPREAD_GATEWAY=10.0.0.1 \ peer-cli-run alpha The well-known macvlan limitation that the host cannot reach its own macvlan children over the network is intentionally not worked around: agents drive each peer through `docker run -i` stdin/stdout, which is the docker control socket, not the LAN. Host-to-peer connectivity is not part of the mental model and is not needed for any current test scenario. Test Plan: - `just peer-cli-image` - `docker network create -d macvlan ... lanspread` succeeds on a host with the default `eth0` interface (or with overridden variables on others). - `just peer-cli-run alpha` and `just peer-cli-run beta` in two terminals; both containers come up on the LAN with distinct IPs and discover each other via mDNS without any `connect` command. Co-Authored-By: Claude Opus 4.7 (1M context) --- justfile | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/justfile b/justfile index 6a0621e..f84ec2e 100644 --- a/justfile +++ b/justfile @@ -31,9 +31,25 @@ peer-cli-build: peer-cli-image: docker build -f crates/lanspread-peer-cli/Dockerfile -t lanspread-peer-cli:dev . -peer-cli-run NAME: +# macvlan: each peer container gets its own MAC/IP on the real LAN. +# Override on the command line if your LAN differs, e.g. +# just LANSPREAD_PARENT_IFACE=enp4s0 LANSPREAD_SUBNET=10.0.0.0/24 LANSPREAD_GATEWAY=10.0.0.1 peer-cli-net +LANSPREAD_NET := "lanspread" +LANSPREAD_PARENT_IFACE := "eth0" +LANSPREAD_SUBNET := "192.168.1.0/24" +LANSPREAD_GATEWAY := "192.168.1.1" + +peer-cli-net: + docker network inspect {{LANSPREAD_NET}} >/dev/null 2>&1 || \ + docker network create -d macvlan \ + --subnet={{LANSPREAD_SUBNET}} \ + --gateway={{LANSPREAD_GATEWAY}} \ + -o parent={{LANSPREAD_PARENT_IFACE}} \ + {{LANSPREAD_NET}} + +peer-cli-run NAME: peer-cli-net mkdir -p "target/peer-cli/{{NAME}}/state" "target/peer-cli/{{NAME}}/games" - docker run --rm --init --network host --name "lanspread-peer-cli-{{NAME}}" -i \ + docker run --rm --init --network {{LANSPREAD_NET}} --name "lanspread-peer-cli-{{NAME}}" -i \ -v "$PWD/target/peer-cli/{{NAME}}/state:/state" \ -v "$PWD/target/peer-cli/{{NAME}}/games:/games" \ lanspread-peer-cli:dev \