From 859cb256f302a86adfa9b0fa550a7c2d385de3ae Mon Sep 17 00:00:00 2001 From: atlas Date: Wed, 10 Jun 2026 21:34:14 +0200 Subject: [PATCH] docs(network): document container-side route + resolver wiring for isolation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "What the Rust side does" section predated the isolated-container egress fix and omitted two now-implemented mechanisms: - HOST_ADDRESS= in the nspawn conf — load-bearing for the container's default route (nixos-container only installs `default via` when HOST_ADDRESS is non-empty; bridge mode skips host-side route setup). - the resolver wiring: hive-priv's bridge-DNS marker + the hyperhive-isolated-dns oneshot rewriting resolv.conf to the bridge dnsmasq (nixos-container re-copies the host's resolv.conf each start, so the fix is runtime, ordered before the harness). Doc-only; brings network.md in line with the merged behavior. --- docs/network.md | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/docs/network.md b/docs/network.md index e0d2013c..0f3d9330 100644 --- a/docs/network.md +++ b/docs/network.md @@ -125,12 +125,43 @@ address arithmetic. ### What the Rust side does `hive-c0re` reads `HIVE_NETWORK_ISOLATION` and, when set, passes -`PRIVATE_NETWORK=1`, `LOCAL_ADDRESS=`, and -`HOST_BRIDGE=` via `lifecycle::set_nspawn_flags` when -creating or updating containers. Each agent gets a deterministic IP -derived from its name so the address is reproducible across destroy/recreate. -This applies uniformly to all containers including the manager — no special -case. +`PRIVATE_NETWORK=1`, `LOCAL_ADDRESS=`, +`HOST_ADDRESS=`, and `HOST_BRIDGE=` via +`lifecycle::set_nspawn_flags` when creating or updating containers. Each +agent gets a deterministic IP derived from its name so the address is +reproducible across destroy/recreate. This applies uniformly to all +containers including the manager — no special case. + +`HOST_ADDRESS` is the bridge gateway IP (the address part of +`HIVE_NETWORK_SUBNET`, via `lifecycle::bridge_gateway_ip` — taken verbatim +so a non-`.1` operator override still resolves to wherever the bridge +actually lives). It is **load-bearing**: nixos-container's container-side +network setup only installs a default route (`ip route add default via +$HOST_ADDRESS`) when `HOST_ADDRESS` is non-empty. In bridge mode the +host-side address/route setup is skipped, so writing it only affects the +container's default route — without it the container comes up with an IP +but no path off the bridge subnet (no internet, no `api.anthropic.com`). + +### How the isolated container gets its resolver + +nixos-container copies the **host's** `/etc/resolv.conf` into the container +at every start. The host resolver (e.g. `127.0.0.53` from systemd-resolved, +or a LAN router) is unreachable from a private netns and isn't +authoritative for the hive's own zones, so it must be replaced with the +bridge dnsmasq (the gateway IP). Because the copy happens on every start, a +declarative `environment.etc."resolv.conf"` would be clobbered — so the +wiring is runtime: + +- `hive-priv` drops a marker file (`/etc/hyperhive-bridge-dns`, carrying the + gateway IP) into the container's `/etc` **only when isolated**, removing + it otherwise — so one shared container toplevel behaves correctly in both + netns modes. +- the `hyperhive-isolated-dns` oneshot (harness-base.nix), gated on that + marker, rewrites `/etc/resolv.conf` to `nameserver ` at boot. + It is ordered `before` the harness (`hive-ag3nt`), the matrix daemon, and + `tea-login` so the resolver is correct before the first DNS lookup; it's + an instant no-op in shared-netns mode (the marker is absent, so + `ConditionPathExists` skips it). **Why isolation is safe for the manager**: all hive-c0re communication goes through unix domain sockets (`/run/hive/mcp.sock` for agent requests,