argus on PR #832:
- 🟡 spawn_poll was inserted BETWEEN write's closing doc and the
pub fn write line; rust treated the consecutive /// as one block,
so spawn_poll inherited write's tail and write ended up with no
closing doc. moved spawn_poll AFTER write to fix attribution.
- 🟡 spawn_poll(coord) took Arc<Coordinator> just to drop it
immediately. dropped the param; main.rs call site now just
agent_sockets::spawn_poll().
no functional change. 10 tests still pass.
closes the gate atlas raised on PR #813: without per-agent opt-in
signal, agent-sockets.json listed every sub-agent, and any agent
that hadn't flipped hyperhive.web.useUnixSocket would 502 the
gateway (its harness still binds TCP, no socket at the published
path).
harness side (web_ui::bind_unix):
- after successful bind + chmod, drop a `.bound` marker in the
per-agent dir as a stable 'this agent has a unix socket here'
signal. best-effort: a failed marker write logs at WARN but
doesn't abort serve (the socket still binds fine; gateway just
keeps using TCP for one more poll).
c0re side (agent_sockets):
- new READY_MARKER const + ready_marker_for(name) helper
- build_map filters by ready_marker_for(name).exists() — only agents
whose harness has bound the socket appear in the JSON map
- new build_map_with<F> internal extracts the predicate so tests
pass a controlled is_ready closure (no real fs access)
- new spawn_poll() background task: re-fires agent_sockets::write
every 10s so the JSON catches up to fresh markers without
needing a container-start hook. write() idempotency means
steady-state cost is one stat per agent per tick.
10 tests: 6 prior + new build_map_filters_by_ready_predicate +
ready_marker_path_is_sibling_of_socket. existing tests adjusted to
call build_map_with(_, |_| true) since the default path now hits
the fs.
once this lands + #822 lands, atlas's gateway-side step 3 can drop
its eval-time `pathExists` fallback — c0re only publishes opted-in
agents, so the gateway can trust the JSON unconditionally.
was: /run/hive-agent/<name>.sock (flat single-file bind-mount).
issue: file bind-mounts don't survive the harness's 'unlink stale
socket then bind(2) a new one' cycle. The unlink drops the bind
inside the container; the rebind happens in private container
namespace; host never sees the new inode → gateway can't connect.
now: /run/hive-agent/<name>/web.sock (per-agent SUBDIR + fixed
filename). Lifecycle bind-mounts the parent dir per agent (step 2b)
so both sides see the same dir inode; the socket appears on the
host the moment the harness binds it.
new helpers:
- AGENT_SOCKET_DIR const (parent, gateway binds this whole tree)
- SOCKET_FILENAME const ("web.sock")
- agent_dir_for(name) (per-agent subdir, lifecycle bind-mounts this)
- socket_path_for(name) (= agent_dir_for(name).join(SOCKET_FILENAME))
per-agent dir isolation also satisfies mara on #800 directly:
agent's container only sees its own subdir + socket, never siblings'.
8 tests now (added agent_dir_for_is_socket_parent invariant).
sibling to agent_ports.rs (#748 / #15). Writes
/var/lib/hyperhive/agent-sockets.json mapping agent name → unix
socket path under /run/hive-agent/<name>.sock — the source of truth
for the gateway's eventual unix-socket upstreams once it flips off
TCP per #784's transition plan.
shape mirrors agent_ports.rs:
- BTreeMap for deterministic JSON output
- atomic <path>.tmp + rename for partial-write safety
- idempotent (skip rewrite when content unchanged)
- MANAGER_NAME filter (gateway routes manager via dashboard upstream,
not /agent/<n>/)
socket_path_for(name) is pure-deterministic so a name alone resolves
to the same path the c0re writes + the harness binds via
HIVE_WEB_SOCKET (post-PR #800).
hooked into meta::sync_agents alongside the existing
agent_ports::write call (same best-effort + non-fatal shape).
next: step 2b (lifecycle bind-mount /run/hive-agent/ into agent +
gateway containers, with per-agent isolation per mara's #800 nudge
'agents can only access their own sockets'). then atlas's step 3
(gateway proxy_pass http://unix:...). then step 4 drops TCP.