Watch
0
0
Fork
You've already forked hyperhive
0

archetypes: prefill the create form from flake-declared agent archetypes

A flake declares agent archetypes in its `hyperhiveArchetypes` output,
`<name> = { module; meta; }`. The hyperhive flake ships `reviewer`
(default permissions without `execution`).

swarm-builder answers `$SWARM.builder.archetypes` with the `meta` of
every archetype its own hyperhive source declares. swarm-controller asks
once the queue connects and every 10 minutes, checks each archetype as
`POST /api/agents` checks its settings, and stores the result in the
`agent-archetypes` KV bucket when it changed. `GET /api/archetypes`
serves it, and the create form's archetype select prefills permissions,
resource caps and inference from it.

The agent created is an ordinary agent with its own config repo; the
archetype's module is not applied yet.

Refs #5386
This commit is contained in:
atlas 2026-10-17 00:24:08 +02:00 • committed by mara
commit 51035eac1d
22 changed files with 1244 additions and 30 deletions

View file

@ -1,8 +1,8 @@
//! The swarm builder's builds: the subject swarm-controller sends a build on,
//! and what travels each way.
//! The swarm builder's requests: the subjects swarm-controller sends a build
//! and an archetype evaluation on, and what travels each way.
//!
//! Three crates name these: the controller sends the request, the builder
//! answers it, and the auth-callout responder grants the one subject to each.
//! Three crates name these: the controller sends the requests, the builder
//! answers them, and the auth-callout responder grants the subjects to each.
use std::time::Duration;
@ -49,6 +49,35 @@ pub enum Reply {
Failed { error_tail: String },
}
/// The subject an archetype evaluation goes to: the controller asks, the
/// builder evaluates the `meta` of every `hyperhiveArchetypes` entry of its own
/// hyperhive source and answers on the request's reply subject.
pub const ARCHETYPES_SUBJECT: &str = "$SWARM.builder.archetypes";
/// The longest one archetype evaluation may take, and how long the controller
/// waits for its reply.
pub const ARCHETYPES_DEADLINE: Duration = Duration::from_mins(2);
/// One archetype evaluation. No field yet: the builder evaluates the hyperhive
/// source it was deployed with.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct EvalArchetypes {}
/// The builder's answer to one [`EvalArchetypes`].
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "state", rename_all = "kebab-case")]
pub enum ArchetypesReply {
/// `source` is the flake evaluated; `archetypes` maps each archetype's
/// name to its `meta`, as `nix eval --json` printed it and unchecked.
Evaluated {
source: String,
archetypes: serde_json::Map<String, serde_json::Value>,
},
/// The evaluation did not finish: the last lines `nix` printed, or why the
/// builder could not start it.
Failed { error_tail: String },
}
#[cfg(test)]
mod tests {
use super::*;
@ -72,4 +101,19 @@ mod tests {
fn the_reply_deadline_outlasts_the_build() {
assert!(REPLY_DEADLINE > BUILD_DEADLINE);
}
#[test]
fn the_archetypes_reply_is_tagged_by_state() {
let json = serde_json::to_value(ArchetypesReply::Evaluated {
source: "/nix/store/h".to_owned(),
archetypes: serde_json::Map::new(),
})
.expect("encodes");
assert_eq!(json["state"], "evaluated");
let json = serde_json::to_value(ArchetypesReply::Failed {
error_tail: "e".to_owned(),
})
.expect("encodes");
assert_eq!(json["state"], "failed");
}
}