Watch
0
0
Fork
You've already forked hyperhive
0

swarm UI: send a message to an agent, without its hive

The agent view in the swarm UI gets a message box. The text reaches the
agent's harness straight from swarm-controller over the swarm queue and
becomes an inbox turn from the operator's authelia username. hive-c0re is
not in the path.

- gateway: `/api/` forwards authelia's `Remote-User`, replacing any the
  client sent.
- swarm-controller: `POST /api/agents/{name}/input` takes that header as
  the sender (401 without it) and sends `{from, body}` as a NATS request
  on `$SWARM.input.<agent>`. No subscriber is a 503.
- swarm-nats-auth: the controller is granted publish on `$SWARM.input.*`;
  no hive or agent grant names the family, so the harness can trust
  `from`.
- hive-agent: a task subscribes to the agent's own input subject and
  pushes each message into the serve loop's existing reminder channel,
  renamed `local_msg_*` since it now carries both.

The harness does not reply to the request: no agent grant covers a reply
subject. The controller therefore answers 202 once the request times out
(2s) and 503 on the server's no-responders status.

Closes #4828
This commit is contained in:
atlas 2026-10-04 03:00:05 +02:00
commit 48ac4ac423
13 changed files with 664 additions and 24 deletions

View file

@ -251,6 +251,36 @@ pub struct DeployRequest {
pub rev: Option<String>,
}
/// The subject the swarm controller sends an operator's input for `agent`
/// on. That agent's harness subscribes to its own and turns each message into
/// an inbox turn.
///
/// Its own family rather than a leaf under `$SWARM.term.`: every agent on a
/// hive's shared credential may publish under `$SWARM.term.<hive>.>`, and the
/// harness trusts [`AgentInput::from`] only because the controller is the one
/// client granted publish here.
#[must_use]
pub fn agent_input_subject(agent: &str) -> String {
format!("{AGENT_INPUT_SUBJECT_PREFIX}.{agent}")
}
/// The publish grant covering every [`agent_input_subject`], for the one
/// client that may send them. A wildcard for the same no-roster reason as
/// [`DEPLOY_SUBJECT_WILDCARD`].
pub const AGENT_INPUT_SUBJECT_WILDCARD: &str = "$SWARM.input.*";
/// Shared by [`agent_input_subject`] and [`AGENT_INPUT_SUBJECT_WILDCARD`].
const AGENT_INPUT_SUBJECT_PREFIX: &str = "$SWARM.input";
/// What an [`agent_input_subject`] message carries.
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct AgentInput {
/// The operator's authelia username, as the swarm gateway reported it.
pub from: String,
/// The text, verbatim.
pub body: String,
}
/// The hive-notices stream, shared by the hive that publishes and
/// whatever eventually consumes it. Behind the `notices` feature, same
/// reason `status` is behind `kv` — see the module doc.