docs(network): document container-side route + resolver wiring for isolation

The "What the Rust side does" section predated the isolated-container
egress fix and omitted two now-implemented mechanisms:

- HOST_ADDRESS=<bridge-ip> 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.
This commit is contained in:
atlas 2026-06-10 21:34:14 +02:00 committed by mara
commit 859cb256f3

View file

@ -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=<deterministic-ip>`, and
`HOST_BRIDGE=<bridgeName>` 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=<deterministic-ip>`,
`HOST_ADDRESS=<bridge-ip>`, and `HOST_BRIDGE=<bridgeName>` 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 <gateway-ip>` 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,