hive-c0re: drop the dead manager exclusion from the agent web-port map

This commit is contained in:
damocles 2026-06-21 23:19:03 +02:00 committed by mara
commit 0df52806f9
2 changed files with 17 additions and 18 deletions

View file

@ -131,11 +131,11 @@ logical agent name → TCP web port:
Written alongside `agents.conf` on every topology change. Ports come from Written alongside `agents.conf` on every topology change. Ports come from
`lifecycle::agent_web_port(name)` — a pure FNV-1a hash of the name, `lifecycle::agent_web_port(name)` — a pure FNV-1a hash of the name,
reproducible from the name alone. The manager is excluded: it always reproducible from the name alone. Every agent gets an entry — no name is
uses a unix socket (`HIVE_WEB_SOCKET` is unconditionally set for the special-cased. Note this TCP map is now a fallback the gateway no longer
manager role), so its TCP port never appears in the fallback map. reaches: all agents bind a unix-socket web UI (`HIVE_WEB_SOCKET`) and the
The gateway routes `/agent/root/` to the manager's unix socket via gateway routes via `agent-sockets.json`, picking the socket upstream
`agents.conf` alongside sub-agents. whenever one exists. The root agent's UI is routed at `/agent/root/`.
The file doubles as a human-readable audit artifact — `cat agent-ports.json` The file doubles as a human-readable audit artifact — `cat agent-ports.json`
shows every registered sub-agent and its deterministic port assignment. TCP shows every registered sub-agent and its deterministic port assignment. TCP

View file

@ -1,23 +1,24 @@
//! `/var/lib/hyperhive/run/agent-ports.json` writer — flat map of //! `/var/lib/hyperhive/run/agent-ports.json` writer — flat map of
//! agent name → TCP web port. Written alongside `agents.conf` on //! agent name → TCP web port. Written alongside `agents.conf` on
//! every topology change. JSON shape, port derivation (FNV-1a hash), //! every topology change. JSON shape, port derivation (FNV-1a hash),
//! atomicity, and manager exclusion: `docs/gateway.md::Agent port map`. //! and atomicity: `docs/gateway.md::Agent port map`.
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::path::PathBuf; use std::path::PathBuf;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use crate::lifecycle::{self, MANAGER_NAME}; use crate::lifecycle;
#[must_use] #[must_use]
pub fn host_ports_path() -> PathBuf { pub fn host_ports_path() -> PathBuf {
crate::paths::agent_ports_file() crate::paths::agent_ports_file()
} }
/// Compute the agent-port map for the given logical agent names. /// Compute the agent-port map for the given logical agent names. Every
/// Sub-agents only — manager is filtered out at the call boundary /// agent gets an entry — no name is special-cased. (All agents now bind
/// because the gateway doesn't surface per-agent routing for it. /// a unix-socket web UI via `HIVE_WEB_SOCKET`, so this TCP map is a
/// fallback the gateway no longer reaches; tracked for removal.)
/// ///
/// `BTreeMap` keeps the JSON output sorted by key so a re-emit /// `BTreeMap` keeps the JSON output sorted by key so a re-emit
/// without churn produces byte-identical output (helpful when the /// without churn produces byte-identical output (helpful when the
@ -26,7 +27,6 @@ pub fn host_ports_path() -> PathBuf {
pub fn build_map(names: &[String]) -> BTreeMap<String, u16> { pub fn build_map(names: &[String]) -> BTreeMap<String, u16> {
names names
.iter() .iter()
.filter(|n| n.as_str() != MANAGER_NAME)
.map(|n| (n.clone(), lifecycle::agent_web_port(n))) .map(|n| (n.clone(), lifecycle::agent_web_port(n)))
.collect() .collect()
} }
@ -77,17 +77,16 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn build_map_filters_manager() { fn build_map_includes_every_agent() {
// Use MANAGER_NAME in the input so the assert below actually // No name is special-cased: the bootstrap/root container gets a
// exercises the filter path — a literal `"root"` would pass // port entry like any other agent. Use the bootstrap name in the
// trivially if the constant ever changed and the filter // input so this guards against re-introducing an exclusion.
// silently became a no-op. let names: Vec<String> = ["iris", lifecycle::MANAGER_NAME, "argus"]
let names: Vec<String> = ["iris", MANAGER_NAME, "argus"]
.iter() .iter()
.map(|s| (*s).to_owned()) .map(|s| (*s).to_owned())
.collect(); .collect();
let map = build_map(&names); let map = build_map(&names);
assert!(!map.contains_key(MANAGER_NAME)); assert!(map.contains_key(lifecycle::MANAGER_NAME));
assert!(map.contains_key("iris")); assert!(map.contains_key("iris"));
assert!(map.contains_key("argus")); assert!(map.contains_key("argus"));
} }