dashboard + agent UI: surface hive_name + swarm_name in chrome (#701)

Damocles's backend (c41bf1b) landed HYPERHIVE_HIVE_NAME / _SWARM_NAME
env vars and identity.rs accessors. This commit wires them into the two
web surfaces:

hive-c0re/dashboard.rs:
  StateSnapshot gains `hive_name` + `swarm_name` (Option<String>),
  populated from the env vars the c0re NixOS module injects.

hive-ag3nt/web_ui.rs:
  Per-agent StateSnapshot gains the same two fields, populated from
  crate::identity::hive_name() / swarm_name().

Dashboard frontend (index.html + tabs.js):
  A `<p class="banner-thin" id="hive-identity">` sits above the tab
  strip in the chrome. refreshState() populates it with "swarm / hive"
  (or just "hive") when the fields are non-null, and updates
  document.title to "<swarm>/<hive> // h1ve-c0re". No change to
  chrome when both fields are null (backward compat).

Per-agent frontend (index.html + app.js + agent.css):
  A hidden `.agent-hive-label` span under the title row shows the
  swarm/hive label once setHeader() receives non-null names.
  document.title is updated to "<label> // <hive_name>" when hive_name
  is set, enabling browser-tab disambiguation when multiple hives are
  open in parallel. Styled as a small uppercase purple-dim sub-label.
This commit is contained in:
iris 2026-05-31 20:40:12 +02:00 committed by mara
commit e1e5195081
7 changed files with 83 additions and 7 deletions

View file

@ -248,6 +248,16 @@ struct StateSnapshot {
/// local-dev deploys keep working. See `docs/gateway.md::Vhost
/// map`.
gateway_enabled: bool,
/// Human name of this single-host hive instance (e.g. `"pr1ma"`).
/// Sourced from `HYPERHIVE_HIVE_NAME` env var, set by the c0re
/// NixOS module from `services.hyperhive.hiveName`. `None` when
/// the option is unset — chrome falls back to `hostname`.
hive_name: Option<String>,
/// Human name of the wider swarm this hive belongs to (e.g.
/// `"constellat1on"`). Sourced from `HYPERHIVE_SWARM_NAME` env
/// var, set from `services.hyperhive.swarmName`. `None` when
/// unset — chrome omits the swarm segment of the breadcrumb.
swarm_name: Option<String>,
}
/// `OpQuestion` + computed `question_refs` / `answer_refs`. Built
@ -454,6 +464,8 @@ async fn api_state(headers: HeaderMap, State(state): State<AppState>) -> axum::J
let s = v.to_string_lossy().to_ascii_lowercase();
matches!(s.as_str(), "1" | "true" | "yes")
}),
hive_name: std::env::var("HYPERHIVE_HIVE_NAME").ok().filter(|s| !s.is_empty()),
swarm_name: std::env::var("HYPERHIVE_SWARM_NAME").ok().filter(|s| !s.is_empty()),
})
}