Watch
0
0
Fork
You've already forked hyperhive
0

docs: the agent config meta-flake input is the local applied mirror, not forge git+http

config-changes.md § Meta flake, hive-c0re nix comments and the meta.rs/exec.rs
doc comments still described the declared agent-<n> input as a forge-hosted
git+http repo; meta.rs renders git+file://<applied>.
This commit is contained in:
atlas 2026-10-10 00:42:10 +02:00 • committed by mara
commit 295e8af43a
5 changed files with 33 additions and 36 deletions

View file

@ -114,27 +114,26 @@ transient.
The hive-c0re-owned repo at `/var/lib/hyperhive/meta/` The hive-c0re-owned repo at `/var/lib/hyperhive/meta/`
declares one flake input per agent (`agent-<n>.url = declares one flake input per agent (`agent-<n>.url =
"git+http://<forge>/agent-configs/<n>.git"`) and one "git+file://<applied>"`, the agent's local `applied` mirror) and one
`nixosConfigurations.<n>` output per agent. Each output wraps `nixosConfigurations.<n>` output per agent. Each output wraps
`inputs.agent-<n>.nixosModules.default` with the identity + `inputs.agent-<n>.nixosModules.default` with the identity +
`HIVE_PORT` / `HIVE_LABEL` / `HIVE_DASHBOARD_PORT` injection module. `HIVE_PORT` / `HIVE_LABEL` / `HIVE_DASHBOARD_PORT` injection module.
Containers run against `--flake /var/lib/hyperhive/meta#<n>`. Containers run against `--flake /var/lib/hyperhive/meta#<n>`.
The declared input url is the agent's **forge config repo** (the The declared input url is the agent's **local `applied` mirror**
same `agent-configs/<n>` config PRs merge into), so the meta flake (`/var/lib/hyperhive/applied/<n>`) of its forge config repo
references a reviewable, reproducible source rather than a local (`agent-configs/<n>`, the one config PRs merge into). The meta flake
checkout. hive-c0re authenticates that `git+http` fetch via a git therefore resolves entirely from on-disk state: building, and booting,
credential helper that reads the live forge-core token — no token in never depend on the forge being reachable. hive-c0re fills the mirror
the url or the lock. The rebuild paths, however, do **not** re-lock by fast-forwarding `applied/<n>/main` to the merged config commit,
from the forge: they `--override-input agent-<n> reading the forge with the hive's read token
git+file:///var/lib/hyperhive/applied/<n>`, locking the exact config (`forge::reader_token()`); the forge repo is the review and audit
`applied/<n>/main` was fast-forwarded to. That keeps a rebuild surface, not a flake input. The rebuild paths re-lock the input with
reproducible and independent of forge reachability — rebuilds fire on `--override-input agent-<n> git+file:///var/lib/hyperhive/applied/<n>`,
crash-restart and meta bumps, not just config merges — while the locking the exact config `applied/<n>/main` was fast-forwarded to.
declared url stays the forge. `sync_agents` re-renders + re-locks the Rebuilds fire on crash-restart and meta bumps, not just config
persistent input; a plain `nix flake lock` leaves an existing applied merges, so the lock stays on that head. `sync_agents` re-renders and
override in place (it only re-locks when the declared url itself re-locks the persistent input.
changes), so the forge-declared / applied-deployed split is stable.
Lock updates are single-phase and commit when the lock changed: Lock updates are single-phase and commit when the lock changed:
`meta::lock_update_for_rebuild(name)` relocks one agent's input for a `meta::lock_update_for_rebuild(name)` relocks one agent's input for a

View file

@ -884,7 +884,7 @@ fn parse_agent_input_names(inputs: &[String]) -> Vec<String> {
/// validating parser every other agent-name wire field in this codebase /// validating parser every other agent-name wire field in this codebase
/// goes through, so a name that reaches /// goes through, so a name that reaches
/// [`fast_forward_applied_main`](crate::forge::fast_forward_applied_main) or /// [`fast_forward_applied_main`](crate::forge::fast_forward_applied_main) or
/// a `git+http://.../agent-configs/<name>.git` URL built off it has already /// an `agent-configs/<name>` forge URL built off it has already
/// passed the same bar as one that arrived over a socket. Not just a /// passed the same bar as one that arrived over a socket. Not just a
/// helper for [`parse_agent_input_names`]: `run_meta_lock`'s `fanout` /// helper for [`parse_agent_input_names`]: `run_meta_lock`'s `fanout`
/// parameter carries pre-computed agent names too, and while today's only /// parameter carries pre-computed agent names too, and while today's only

View file

@ -235,9 +235,8 @@ pub async fn lock_update_for_rebuild(name: &str) -> Result<()> {
let _guard = META_LOCK.lock().await; let _guard = META_LOCK.lock().await;
let dir = crate::paths::meta_root(); let dir = crate::paths::meta_root();
let input = format!("agent-{name}"); let input = format!("agent-{name}");
// Re-lock from the local applied mirror, not the persistent forge URL: // Re-lock against `applied/<n>/main` explicitly, so the lock is the
// build exactly `applied/<n>/main` and stay reproducible when the forge // exact applied head whatever the declared input url says.
// is unreachable.
let applied = applied_override_url(&crate::paths::applied_dir(name)); let applied = applied_override_url(&crate::paths::applied_dir(name));
nix( nix(
&dir, &dir,
@ -264,12 +263,12 @@ pub async fn lock_update_for_rebuild(name: &str) -> Result<()> {
} }
/// `--override-input` URL re-locking an agent's config input against its /// `--override-input` URL re-locking an agent's config input against its
/// LOCAL applied mirror (`git+file://<applied_dir>`, current `main` head) /// LOCAL applied mirror (`git+file://<applied_dir>`, current `main` head),
/// instead of the persistent forge URL declared in the meta flake. The /// the same url the meta flake declares. The deploy + rebuild paths re-lock
/// deploy + rebuild paths re-lock from here: the applied tree was already /// from here: the applied tree was already fast-forwarded to the reviewed
/// fast-forwarded to the reviewed head, so this builds exactly that config /// head, so this builds exactly that config with no forge access. No
/// and stays reproducible when the forge is unreachable. No `?rev` — `main` /// `?rev` — `main` head is the reviewed head at deploy time. Pure so it's
/// head is the reviewed head at deploy time. Pure so it's unit-testable. /// unit-testable.
fn applied_override_url(applied_dir: &Path) -> String { fn applied_override_url(applied_dir: &Path) -> String {
format!("git+file://{}", applied_dir.display()) format!("git+file://{}", applied_dir.display())
} }
@ -1456,8 +1455,8 @@ mod tests {
#[test] #[test]
fn applied_override_url_targets_local_mirror_main_head() { fn applied_override_url_targets_local_mirror_main_head() {
// Deploy + rebuild re-lock against the local applied mirror's `main` // Deploy + rebuild re-lock against the local applied mirror's `main`
// head (no `?rev`), never the persistent forge URL — so a rebuild // head (no `?rev`) — so a rebuild survives forge unreachability and
// survives forge unreachability and builds the fast-forwarded config. // builds the fast-forwarded config.
let p = Path::new("/var/lib/hyperhive/agents/iris/applied"); let p = Path::new("/var/lib/hyperhive/agents/iris/applied");
let url = applied_override_url(p); let url = applied_override_url(p);
assert_eq!(url, "git+file:///var/lib/hyperhive/agents/iris/applied"); assert_eq!(url, "git+file:///var/lib/hyperhive/agents/iris/applied");

View file

@ -56,9 +56,9 @@ let
username = core username = core
[http] [http]
# Abort a stalled fetch instead of blocking startup indefinitely. If a # Abort a stalled fetch instead of blocking startup indefinitely. If a
# git+http transfer (e.g. `nix flake lock` of the forge-hosted config # git http transfer from the forge (e.g. filling an applied mirror)
# inputs) drops below 1 KiB/s for 30s -- forge unreachable / not ready # drops below 1 KiB/s for 30s -- forge unreachable / not ready on cold
# on cold boot -- git fails fast rather than hanging the whole daemon. # boot -- git fails fast rather than hanging the whole daemon.
# Pairs with GIT_TERMINAL_PROMPT=0 (no credential-prompt hang) and the # Pairs with GIT_TERMINAL_PROMPT=0 (no credential-prompt hang) and the
# migration shellout timeout in migrate.rs. # migration shellout timeout in migrate.rs.
lowSpeedLimit = 1024 lowSpeedLimit = 1024
@ -265,8 +265,8 @@ in
after = [ "hive-c0re.socket" ]; after = [ "hive-c0re.socket" ];
path = [ path = [
pkgs.git pkgs.git
# `git-credential-hive-forge` on PATH so git finds it when nix fetches # `git-credential-hive-forge` on PATH so git finds it for git+http
# the forge-hosted agent-config inputs (helper = hive-forge). # fetches from the forge (helper = hive-forge).
forgeCredHelper forgeCredHelper
"/run/current-system/sw" "/run/current-system/sw"
]; ];

View file

@ -28,9 +28,8 @@ in
# Never let git block on an interactive credential prompt. hive-core is a # Never let git block on an interactive credential prompt. hive-core is a
# TTY-less system user, so a prompt (e.g. the forge credential helper # TTY-less system user, so a prompt (e.g. the forge credential helper
# returns nothing because the forge isn't reachable yet on cold boot) # returns nothing because the forge isn't reachable yet on cold boot)
# would hang forever — this is what froze the whole daemon during startup # would hang forever and freeze the daemon during startup. With this set,
# migration's `nix flake lock` of the forge-hosted config inputs. With # git fails fast instead of prompting. Paired with the git http
# this set, git fails fast instead of prompting. Paired with the git http
# low-speed abort in `safeDirGitconfig` (bounds a stalled transfer) and # low-speed abort in `safeDirGitconfig` (bounds a stalled transfer) and
# the 120s migration shellout timeout in migrate.rs. # the 120s migration shellout timeout in migrate.rs.
GIT_TERMINAL_PROMPT = "0"; GIT_TERMINAL_PROMPT = "0";