Phase 6a: per-container web UI (axum); per-agent port hashed from name

This commit is contained in:
müde 2026-05-14 23:39:06 +02:00
parent 14cb107125
commit d0f954bbc1
9 changed files with 112 additions and 4 deletions

40
hive-ag3nt/src/web_ui.rs Normal file
View file

@ -0,0 +1,40 @@
//! Per-container HTTP UI. Phase 6 minimum — a status page on a host port.
//! Containers share the host's network namespace (privateNetwork = false), so
//! each instance must bind a distinct port. `HIVE_PORT` is set per agent by
//! `hive-c0re`'s generated per-agent flake (deterministic from agent name).
use std::net::SocketAddr;
use anyhow::{Context, Result};
use axum::{Router, extract::State, response::Html, routing::get};
#[derive(Clone)]
struct AppState {
label: String,
}
pub async fn serve(label: String, port: u16) -> Result<()> {
let state = AppState { label };
let app = Router::new().route("/", get(index)).with_state(state);
let addr = SocketAddr::from(([0, 0, 0, 0], port));
let listener = tokio::net::TcpListener::bind(addr)
.await
.with_context(|| format!("bind web UI on port {port}"))?;
tracing::info!(%port, "web UI listening");
axum::serve(listener, app).await?;
Ok(())
}
async fn index(State(state): State<AppState>) -> Html<String> {
Html(format!(
"<!doctype html>\n\
<html>\n\
<head><title>{label}</title></head>\n\
<body>\n\
<h1>{label}</h1>\n\
<p>hyperhive harness placeholder. Phase 6a: this page exists.</p>\n\
</body>\n\
</html>\n",
label = state.label,
))
}