Watch
0
0
Fork
You've already forked hyperhive
0

build agents only from internal/meta; delete the hive's local meta flake

The hive no longer renders, locks or commits a meta flake. A rebuild's
MetaSync node picks the internal/meta commit and records it with the
hyperhive and config pins it holds; Prebuild, Swap and Create build from it,
and a head that does not pin the agent's current config fails the build
naming the agent. hive-priv's meta_rev is required.

Removed: meta.rs sync/lock/render, the manual-rebuild relock, the boot
sweep's hyperhive bump (MetaLock), the update-meta-inputs form and its
panel, MetaWindow, the hive's hyperhive_rev setting.

Kept: agent_set::refresh holds what sync_agents also did (topology.json, the
gateway's agent-socket map and nginx include), called from spawn, destroy,
rebuild and the migration.

The boot reconcile and the stale-start upgrade compare the agent's marker
with the hyperhive pin of internal/meta; the dashboard's deployed chip reads
the config marker a successful build writes.
This commit is contained in:
atlas 2026-10-25 13:33:15 +01:00
commit 4aa11b3a94
64 changed files with 1042 additions and 3793 deletions

View file

@ -8,9 +8,7 @@
//! [`PrivEvent::Line`]s when asked to.
use anyhow::{Context as _, Result, anyhow, bail};
use hive_priv_sock::{
META_DIR, PrivEvent, PrivStream, PrivStreamLine, meta_flake_url, valid_meta_rev,
};
use hive_priv_sock::{PrivEvent, PrivStream, PrivStreamLine, meta_flake_url, valid_meta_rev};
use tokio::io::{AsyncWriteExt, BufReader};
use tokio::net::unix::OwnedWriteHalf;
use tokio::process::Command;
@ -102,7 +100,7 @@ pub(crate) async fn destroy_container(name: &str) -> Result<(String, String)> {
pub(crate) async fn container_flake_action(
verb: &str,
name: &str,
meta_rev: Option<&str>,
meta_rev: &str,
stream: bool,
writer: &mut OwnedWriteHalf,
) -> Result<(String, String)> {
@ -216,14 +214,10 @@ async fn log_and_forward(stdout: &str, stderr: &str, stream: bool, writer: &mut
/// request.
pub(crate) const FORGE_URL_ENV: &str = "HIVE_PRIV_FORGE_URL";
/// The flake agent toplevels are built from: the hive's own meta flake, or
/// `internal/meta` at `meta_rev` when the request names one. The URL is
/// `forge_base`, hive-priv's own; only the rev comes from the request, and it
/// is checked here.
fn meta_flake(meta_rev: Option<&str>, forge_base: Option<&str>) -> Result<String> {
let Some(rev) = meta_rev else {
return Ok(META_DIR.to_owned());
};
/// The flake agent toplevels are built from: `internal/meta` at `rev`. The URL
/// is `forge_base`, hive-priv's own; only the rev comes from the request, and
/// it is checked here.
fn meta_flake(rev: &str, forge_base: Option<&str>) -> Result<String> {
if !valid_meta_rev(rev) {
bail!("meta_rev {rev:?} is not a 40-digit lowercase hex commit id");
}
@ -377,7 +371,7 @@ async fn run_bounded(
/// `PrivEvent::Line` forwarding. Bounded by [`NIX_BUILD_TIMEOUT`].
async fn nix_build_toplevel(
name: &str,
meta_rev: Option<&str>,
meta_rev: &str,
writer: Option<&mut OwnedWriteHalf>,
) -> Result<String> {
let flake = meta_flake(meta_rev, std::env::var(FORGE_URL_ENV).ok().as_deref())?;
@ -726,30 +720,19 @@ mod tests {
/// `hive-c0re`'s own `lifecycle::prebuild_toplevel` construction, since
/// that step's whole point is warming the store for this later build.
/// A drifted attr path defeats the cache-warming silently — no error,
/// just a slower `update`.
#[test]
fn toplevel_attr_matches_prebuild_toplevels_construction() {
assert_eq!(
toplevel_attr(&meta_flake(None, None).unwrap(), "atlas"),
"/var/lib/hyperhive/meta#nixosConfigurations.atlas.config.system.build.toplevel"
);
}
/// The flake is the hive's own unless the request names an `internal/meta`
/// commit, and then its URL is the unit's, never the request's.
/// just a slower `update`. The flake is `internal/meta` at the rev, and its
/// URL is the unit's, never the request's.
#[test]
fn a_meta_rev_builds_from_internal_meta_at_the_units_forge() {
let rev = "c".repeat(40);
let forge = Some("http://forge.t.local");
assert_eq!(
toplevel_attr(&meta_flake(Some(&rev), forge).unwrap(), "atlas"),
toplevel_attr(&meta_flake(&rev, forge).unwrap(), "atlas"),
format!(
"git+http://forge.t.local/internal/meta.git?rev={rev}\
#nixosConfigurations.atlas.config.system.build.toplevel"
)
);
// No rev: the forge is not consulted at all.
assert_eq!(meta_flake(None, forge).unwrap(), hive_priv_sock::META_DIR);
}
/// Nothing but a commit id gets into the ref, whatever the request says.
@ -763,7 +746,7 @@ mod tests {
&format!("{}#evil", "a".repeat(34)),
&format!("{}&ref=x", "a".repeat(34)),
] {
let err = meta_flake(Some(bad), forge).unwrap_err();
let err = meta_flake(bad, forge).unwrap_err();
assert!(
format!("{err:#}").contains("not a 40-digit"),
"{bad}: {err:#}"
@ -775,7 +758,7 @@ mod tests {
fn a_meta_rev_without_a_forge_in_the_unit_is_refused() {
let rev = "c".repeat(40);
for forge in [None, Some("")] {
let err = meta_flake(Some(&rev), forge).unwrap_err();
let err = meta_flake(&rev, forge).unwrap_err();
assert!(format!("{err:#}").contains(FORGE_URL_ENV), "{err:#}");
}
}

View file

@ -353,19 +353,13 @@ async fn exec(
ref name,
stream,
ref meta_rev,
} => {
container::container_flake_action("update", name, meta_rev.as_deref(), stream, writer)
.await
}
} => container::container_flake_action("update", name, meta_rev, stream, writer).await,
PrivRequest::CreateContainer {
ref name,
stream,
ref meta_rev,
} => {
container::container_flake_action("create", name, meta_rev.as_deref(), stream, writer)
.await
}
} => container::container_flake_action("create", name, meta_rev, stream, writer).await,
PrivRequest::DestroyContainer { ref name } => container::destroy_container(name).await,