feat(#2363): full-DHCP for all agents — drop static agent_network_ip

All agent containers now receive their bridge IP dynamically via DHCP
from the dnsmasq pool instead of a hash-derived static address:

- nix/templates/harness-base.nix: networking.useDHCP = true
- nix/modules/hive-gateway.nix: expand DHCP pool to full usable range
  (.2 to .254 on /24) — was last-14-IPs-only
- hive-sh4re/src/priv_proto.rs: remove agent_ip from NetworkIsolation
- hive-c0re/src/lifecycle/mod.rs: drop agent_network_ip + DHCP_POOL_SIZE
- hive-c0re/src/lifecycle/host_config.rs: remove agent_network_ip call
- hive-priv/src/main.rs: LOCAL_ADDRESS= empty (DHCP assigns IP);
  HOST_ADDRESS still set so nixos-container installs default route
  before the DHCP lease arrives
- nix/dhcp-pool-size: deleted (no longer needed)

The nix/dhcp-pool-size single-source-of-truth file and all associated
Rust/Nix dual-constant plumbing are gone — there is no static map.
bridge_gateway_ip() is retained (still needed for HOST_ADDRESS).

Closes #2363
This commit is contained in:
atlas 2026-07-13 11:18:42 +02:00 committed by mara
commit 4cdbbafc44
8 changed files with 37 additions and 244 deletions

View file

@ -11,17 +11,12 @@ let
forgeCfg = config.services.hyperhive.forge;
networkCfg = config.services.hyperhive.network;
# DHCP pool for bridge-attached service containers (hive-ci, etc.).
# Occupies the last dhcpPoolSize usable addresses of the subnet
# (e.g. .241-.254 on a /24). Agent containers use hash-derived static
# IPs (lifecycle::agent_network_ip) and are excluded from this range
# by subtracting dhcpPoolSize from the usable count before hashing
# (agent_slots = usable - dhcpPoolSize), so agents only ever land in
# [.2, .(usable-dhcpPoolSize+1)] by construction.
# DHCP pool covering all usable host addresses on the bridge subnet.
# All containers (agents and service containers such as hive-ci) receive
# their IPs dynamically; there are no hash-derived static assignments.
# Range: .2 to .(hostCount-2) — skipping .0 (network), .1 (gateway/host
# bridge), and the broadcast address.
#
# Single source of truth: `nix/dhcp-pool-size` (one integer).
# Rust reads it at compile time via `include_bytes!` in lifecycle/mod.rs.
dhcpPoolSize = lib.strings.toIntBase10 (lib.strings.trim (builtins.readFile ../dhcp-pool-size));
# IPv4 helpers — nix integers are 64-bit so all /0-/32 values are safe.
ipToInt =
ip:
@ -42,9 +37,9 @@ let
hostCount = pow2 (32 - networkCfg.bridgePrefixLength);
# Mask off host bits to get the network base address.
networkBase = builtins.bitAnd (ipToInt networkCfg.bridgeIp) (4294967295 - hostCount + 1);
# DHCP range: last dhcpPoolSize usable host addresses (broadcast - 1 down).
# DHCP range: .2 (first usable after gateway) to .(hostCount-2) (last usable).
dhcpStart = intToIp (networkBase + 2); # skip .0 (network) and .1 (gateway)
dhcpEnd = intToIp (networkBase + hostCount - 2); # last usable = broadcast - 1
dhcpStart = intToIp (networkBase + hostCount - 1 - dhcpPoolSize); # dhcpEnd - poolSize + 1
# Dashboard SPA dist, static-served by nginx below. Read in OUTER scope so
# `config` is the host's (inside the container block it'd be the container's).
@ -1034,11 +1029,11 @@ in
++ lib.optional (
matrixCfg.enable && matrixCfg.gatewayHost != null
) "/${matrixCfg.gatewayHost}/${networkCfg.bridgeIp}";
# DHCP pool for bridge-attached service containers (hive-ci, etc.).
# Range is computed from the bridgeIp/bridgePrefixLength at eval
# time; the last dhcpPoolSize usable host addresses are reserved.
# Agent containers hash into agent_slots = usable - dhcpPoolSize
# so they never land here (excluded by construction, not remapping).
# DHCP pool covering all usable host addresses on the bridge subnet.
# Range is computed from bridgeIp/bridgePrefixLength at eval time:
# .2 (first after gateway) to .(hostCount-2) (last usable before
# broadcast). All containers — agents and service containers alike —
# receive their IPs dynamically from this pool.
dhcp-range = "${dhcpStart},${dhcpEnd},1h";
dhcp-leasefile = "/var/lib/dnsmasq/dnsmasq.leases";
};