lifecycle: drop manager port special case (#753) — manager hashes into 8100..8999 like every other agent
This commit is contained in:
parent
f9a492f7a9
commit
4526e40a49
11 changed files with 43 additions and 39 deletions
|
|
@ -21,8 +21,8 @@ the substrate.
|
|||
host (NixOS, runs hive-c0re.service)
|
||||
│
|
||||
├── operator
|
||||
│ ├── browser → :7000 hive-c0re dashboard
|
||||
│ ├── browser → :8000 / :8100-8999 per-agent web UIs
|
||||
│ ├── browser → :7000 hive-c0re dashboard
|
||||
│ ├── browser → :8100-8999 per-agent web UIs (all agents, FNV-1a hash)
|
||||
│ └── CLI → /run/hyperhive/host.sock admin protocol
|
||||
│
|
||||
├── hive-c0re (Rust daemon: lifecycle / broker / approvals /
|
||||
|
|
|
|||
|
|
@ -343,7 +343,8 @@ Differences from sub-agents:
|
|||
- `flake.nix` extends `hyperhive.nixosConfigurations.manager`
|
||||
(vs `agent-base`).
|
||||
- Container name is `hm1nd` (no `h-` prefix).
|
||||
- Fixed web UI port (`MANAGER_PORT = 8000`).
|
||||
- Web UI port via `lifecycle::agent_web_port("hm1nd")` — same
|
||||
FNV-1a hash as every other agent (8100..8999 range) since #753.
|
||||
- `set_nspawn_flags` adds two extra binds: `/var/lib/hyperhive/agents`
|
||||
→ `/agents` (RW) so the manager can edit per-agent proposed repos,
|
||||
and `/var/lib/hyperhive/applied` → `/applied` (RO) so the manager
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ exist because something already went wrong without them.
|
|||
- The manager is `hm1nd` (no `h-` prefix, fixed name).
|
||||
- `MAX_AGENT_NAME` in `lifecycle.rs` enforces the cap.
|
||||
- Per-agent web UI port = `WEB_PORT_BASE + FNV1a(name) % WEB_PORT_RANGE`
|
||||
(8100..8999); manager fixed at 8000; dashboard `cfg.dashboardPort`
|
||||
(default 7000).
|
||||
(8100..8999) for every agent including the manager (#753); dashboard
|
||||
`cfg.dashboardPort` (default 7000).
|
||||
|
||||
## Identity = socket
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
# Web UI
|
||||
|
||||
Two web surfaces share the same skeleton: the dashboard (port 7000)
|
||||
and the per-agent UIs (manager on :8000, sub-agents on a hashed
|
||||
:8100-8999). Both are SPAs — `GET /` returns a static shell,
|
||||
and the per-agent UIs (every container — including the manager —
|
||||
hashes into :8100-8999 via `lifecycle::agent_web_port`'s FNV-1a,
|
||||
since #753). Both are SPAs — `GET /` returns a static shell,
|
||||
`/api/state` returns JSON, JS renders. No full-page reloads.
|
||||
|
||||
## Shape (shared by both)
|
||||
|
|
|
|||
|
|
@ -20,10 +20,13 @@
|
|||
//!
|
||||
//! Ports come from [`crate::lifecycle::agent_web_port`] — pure
|
||||
//! FNV-1a(name) hash so the value is reproducible from the name
|
||||
//! alone. The manager is intentionally excluded: it sits at the
|
||||
//! fixed `MANAGER_PORT` (8000) and the gateway routes `/` straight
|
||||
//! to it without a per-agent prefix (see `nix/modules/hive-gateway.nix`
|
||||
//! upstream config, atlas's #740).
|
||||
//! alone. The manager is intentionally excluded from the map: the
|
||||
//! gateway routes `/` straight to it via the c0re dashboard upstream
|
||||
//! (see `nix/modules/hive-gateway.nix`, atlas's #740) rather than a
|
||||
//! per-agent `/agent/<name>/` prefix. Post-#753 the manager's port
|
||||
//! is computed by the same hash as every other agent, but it still
|
||||
//! doesn't appear here — keeping the routing surface "sub-agents
|
||||
//! only" matches the gateway's current shape.
|
||||
//!
|
||||
//! Atomicity: write to a sibling `.tmp` file + rename so a partial
|
||||
//! write never leaves an unparseable file in place. The gateway's
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@ use crate::container_view::{ContainerView, claude_has_session};
|
|||
use crate::coordinator::Coordinator;
|
||||
use crate::lifecycle::{self, MANAGER_NAME};
|
||||
|
||||
const MANAGER_PORT: u16 = 8000;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AppState {
|
||||
coord: Arc<Coordinator>,
|
||||
|
|
@ -416,7 +414,7 @@ async fn api_state(headers: HeaderMap, State(state): State<AppState>) -> axum::J
|
|||
axum::Json(StateSnapshot {
|
||||
seq,
|
||||
hostname,
|
||||
manager_port: MANAGER_PORT,
|
||||
manager_port: lifecycle::agent_web_port(MANAGER_NAME),
|
||||
any_stale,
|
||||
containers,
|
||||
transients,
|
||||
|
|
|
|||
|
|
@ -16,9 +16,6 @@ pub const MAX_AGENT_NAME: usize = 9;
|
|||
/// nixosConfiguration (`manager`, not `agent-base`).
|
||||
pub const MANAGER_NAME: &str = "hm1nd";
|
||||
|
||||
/// Web UI port reserved for the manager (sub-agents hash into 8100..8999).
|
||||
pub const MANAGER_PORT: u16 = 8000;
|
||||
|
||||
/// Mount point of the per-agent runtime directory inside the container.
|
||||
pub const CONTAINER_RUNTIME_MOUNT: &str = "/run/hive";
|
||||
|
||||
|
|
@ -57,18 +54,16 @@ const WEB_PORT_RANGE: u16 = 900;
|
|||
const DEFAULT_MEMORY_MAX: &str = "2G";
|
||||
const DEFAULT_CPU_QUOTA: &str = "50%";
|
||||
|
||||
/// Per-agent web UI port. Manager is fixed at `MANAGER_PORT`; every
|
||||
/// sub-agent is `WEB_PORT_BASE + FNV-1a(name) % WEB_PORT_RANGE`,
|
||||
/// pure and reproducible from just the name. Collisions are
|
||||
/// possible (birthday paradox at ~30 agents); the operator resolves
|
||||
/// them by renaming an agent (different hash → different port).
|
||||
/// Stable across hosts, restarts, and dashboard renders — no
|
||||
/// state-file dance.
|
||||
/// Per-agent web UI port — `WEB_PORT_BASE + FNV-1a(name) %
|
||||
/// WEB_PORT_RANGE` for every agent including the manager (#753
|
||||
/// dropped the pre-#753 "manager pinned at 8000" special case so
|
||||
/// the port allocation rule reads the same for every name).
|
||||
/// Collisions are possible (birthday paradox at ~30 agents); the
|
||||
/// operator resolves them by renaming an agent (different hash →
|
||||
/// different port). Stable across hosts, restarts, and dashboard
|
||||
/// renders — no state-file dance.
|
||||
#[must_use]
|
||||
pub fn agent_web_port(name: &str) -> u16 {
|
||||
if name == MANAGER_NAME {
|
||||
return MANAGER_PORT;
|
||||
}
|
||||
let mut hash: u32 = 2_166_136_261;
|
||||
for b in name.bytes() {
|
||||
hash ^= u32::from(b);
|
||||
|
|
|
|||
|
|
@ -239,9 +239,11 @@ in
|
|||
];
|
||||
|
||||
# Per-container web UIs share the host's network namespace and need
|
||||
# their ports reachable when there's no gateway in front. Manager:
|
||||
# 8000. Sub-agents: 8100..8999 (deterministic hash; see
|
||||
# `lifecycle::agent_web_port`).
|
||||
# their ports reachable when there's no gateway in front. Every
|
||||
# container — including the manager (#753 dropped the pre-#753
|
||||
# "manager pinned at 8000" special case) — hashes into
|
||||
# 8100..8999 via `lifecycle::agent_web_port`'s FNV-1a, so a single
|
||||
# range opening covers all of them.
|
||||
#
|
||||
# The dashboard port (`cfg.dashboardPort`, default 7000) is *not*
|
||||
# listed here — since #652 the dashboard binds `127.0.0.1` only,
|
||||
|
|
@ -257,11 +259,8 @@ in
|
|||
# open in the host firewall would defeat the gateway's "single
|
||||
# front door" story (closes #621). Operators who opt out of the
|
||||
# gateway still get those direct ports opened so the legacy
|
||||
# `http://<host>:8100/` flow works.
|
||||
# `http://<host>:<port>/` flow works.
|
||||
networking.firewall = lib.mkIf (!config.services.hyperhive.gateway.enable) {
|
||||
allowedTCPPorts = [
|
||||
8000
|
||||
];
|
||||
allowedTCPPortRanges = [
|
||||
{
|
||||
from = 8100;
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ in
|
|||
default = 3000;
|
||||
description = ''
|
||||
TCP port the forge serves HTTP on. Default 3000 sits outside
|
||||
hyperhive's claimed ranges (dashboard 7000, manager 8000,
|
||||
sub-agents 8100..8999). Change this if you already have
|
||||
hyperhive's claimed ranges (dashboard 7000, every agent in
|
||||
8100..8999 via FNV-1a hash). Change this if you already have
|
||||
another forgejo bound to 3000.
|
||||
'';
|
||||
};
|
||||
|
|
|
|||
|
|
@ -246,8 +246,8 @@ in
|
|||
description = ''
|
||||
TCP port tuwunel serves the matrix client-server API on.
|
||||
Default 8008 is the matrix-spec well-known port. Sits
|
||||
outside hyperhive's claimed ranges (dashboard 7000, manager
|
||||
8000, sub-agents 8100..8999). Federation listens on
|
||||
outside hyperhive's claimed ranges (dashboard 7000, every
|
||||
agent in 8100..8999 via FNV-1a hash). Federation listens on
|
||||
`federationPort` separately.
|
||||
'';
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1309,7 +1309,14 @@ in
|
|||
# `applied/hm1nd/flake.nix` (see `lifecycle::setup_applied`);
|
||||
# the values here keep the container sensible if anyone
|
||||
# evaluates the standalone config.
|
||||
HIVE_PORT = "8000";
|
||||
#
|
||||
# `HIVE_PORT` = FNV-1a("hm1nd") % 900 + 8100 = 8875 per
|
||||
# `lifecycle::agent_web_port` (#753 dropped the
|
||||
# pre-#753 "manager pinned at 8000" special case). Hardcoded
|
||||
# here because the standalone-eval path doesn't go through
|
||||
# `meta::render_flake`; real deploys pick up the rust-computed
|
||||
# value via meta and never touch this fallback.
|
||||
HIVE_PORT = "8875";
|
||||
HIVE_LABEL = "hm1nd";
|
||||
};
|
||||
serviceConfig = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue