Watch
0
0
Fork
You've already forked hyperhive
0
Commit graph hyperhive/swarmctl/src/agent.rs
Author SHA1 Message Date
atlas
0831b85b73 swarmctl shares the create-agent, inference and permissions shapes with the controller 2026-10-19 18:58:00 +02:00
atlas
456cf9a7cb set an agent's initial agents.md at create 2026-10-18 15:54:14 +02:00
atlas
98501d2d88 take the agent-create and forge-token responses from swarm-controller-api
swarmctl decoded POST /api/agents and POST /api/agents/{name}/forge-token
into hand-written copies of the controller's private response structs.
Both now live in swarm-controller-api, so a field renamed on one side is a
compile error on the other. Wire format and OpenAPI schema are unchanged.

The request side stays mirrored: CreateAgentRequest carries inference
settings and an ArchetypeRef (swarm-queue-client types) and Permission
(hive-sh4re), and the permissions body is a list of Permission. Where
those types live is #5471's decision.

Files:
- swarm-controller-api/src/agents.rs, lib.rs: the two responses, with
  round-trip tests whose literals come from the tests that move here.
- swarm-controller/src/main.rs: imports them; the struct definitions and
  create_agent_warning_tests (now in the api crate) go.
- swarmctl/src/agent.rs: imports them; the mirrors and their decode tests
  go, and the module doc says only the request is mirrored now.
- swarmctl/src/permissions.rs: its doc pointed at agent.rs for why it
  mirrors; that reason no longer covers Permissions, so it gives its own.
- swarmctl/README.md, swarm-controller-api/README.md, CLAUDE.md: which
  bodies are shared and which are mirrored.

Part of #5472
2026-10-18 00:14:02 +02:00
atlas
be92bc0835 share the forge and schedule wire types through swarm-controller-api
swarmctl hand-mirrored the controller's forge make-admin response and
the /api/schedules bodies. Both now import them from a new serde-only
crate, swarm-controller-api, the way hivectl and hive-c0re share
hive-host-sock. ToSchema sits behind its schema feature, which only
the controller turns on.

The wire format is unchanged: round-trip tests in the new crate pin
each type against the JSON the controller and swarmctl produced, and
the controller's OpenAPI document for these routes is byte-identical.

Files: swarm-controller-api/ is the new crate; swarm-controller and
swarmctl (src, Cargo.toml, Cargo.lock, root Cargo.toml) switch to it;
CLAUDE.md lists the crate and which swarmctl shapes are still local
mirrors; swarmctl/README.md says the forge and schedule bodies come
from the crate, so none of its lines still call them mirrored.

Part of #5472.
2026-10-17 23:27:14 +02:00
atlas
207484dfa6 swarmctl: drop agent mint-identity and its controller route
Re-creating an agent mints its store identity the same way, so the
backfill verb and the `POST /api/agents/{name}/identity` route that only
it called go. The `MintAgentIdentity` job node stays: agent creation and
the renewal pass still queue it.

Part of #4765 (c).
2026-10-13 08:08:06 +02:00
atlas
2df19e4899 swarmctl: log in with the operator's own SSO account and reach the controller through the swarm domain
`swarmctl --swarm https://<swarm> login` runs the OAuth 2.0
authorization-code flow with PKCE against the swarm's authelia as a new
public `swarmctl` client: a pushed authorization request, the browser login
and explicit consent, and the code delivered by form_post to a loopback
port. The access and refresh tokens are cached at mode 0600 under
$XDG_STATE_HOME/swarmctl/ and refreshed under a file lock, since authelia
rotates the refresh token and revokes the login when a used one is
presented again. With --swarm (or SWARMCTL_SWARM), the agent, schedule and
forge verbs send their requests to https://<swarm>/api/ with the bearer
token instead of over the controller's unix socket, which stays the default.

authelia registers the client on every authelia host with a swarm domain:
`kind = "native"`, public, no minted secret, scopes offline_access and
authelia.bearer.authz, audience https://<swarm>/api/ (matched as a URL
prefix), redirect http://127.0.0.1/callback (any loopback port). Every
field is one authelia 4.39.20's validator requires for a public client
holding the bearer scope.

The swarm apex's /api/ answers a refused caller that sent an
Authorization header with the 401 itself instead of the login redirect,
so the CLI can tell "log in again" apart from an answer; a browser still
gets the redirect. The bearer token is not forwarded to the controller.

Refs #4409 (mara: c94764, c94782).
2026-10-12 01:25:58 +02:00
atlas
a0b1846006 swarm: named scheduled prompts for one or more agents, fired in each agent
swarm-controller keeps every scheduled prompt (id, name, agents, body,
anchor, interval, paused) as one document under one key of a new
`schedule-definitions` KV bucket, so each change is a single
compare-and-set and a prompt exists for all of its agents or none. It
writes each agent's key of a new `agent-schedules` bucket as the prompts
naming that agent, without names or other agents, after every change and
on a one-minute reconcile pass. It is the only writer of both, behind
GET/POST /api/schedules and PATCH/DELETE /api/schedules/{id}.
`swarmctl schedule add|list|edit|pause|resume|rm` drives those routes over
the controller socket.

An agent's own queue credential is granted one more subject, the direct
get of its own key in `agent-schedules`. A KV watch is not granted: its
consumer create subject carries no key, so it would cover every agent's.

hive-agent's new schedule ticker reads that key every 10 seconds and fires
due slots as the same `schedule:<id>` todo hive-c0re's worker pushes. Fire
times are anchor + k*interval; the last fired slot per schedule id is kept
in the state db, so a body edit or list refresh never moves or refires a
slot, downtime fires the latest missed slot once, a restart refires
nothing, a one-shot fires once, and paused slots are skipped.

Refs #4914
2026-10-04 17:24:57 +02:00
atlas
fc97c237dc swarm-queue-client: one agent-token spelling, and no hive in AgentCredential
`swarm_queue_client::agent_token::format_agent_token` / `parse_agent_token`
are the spelling an agent presents its own queue secret in,
`swarm-agent.<agent>.<secret>`, and the one the auth-callout responder
reads back. The prefix is what separates it from an OIDC access token,
which may itself contain `.`. Parsing distinguishes "not an agent token"
(no prefix) from "a malformed one"; the error names the problem and never
the value. The module is store-free, so the agent formats its token
without linking the secret-store client.

`swarm_secret_client::queue::AgentCredential` loses `hive`: an agent's
identity is not tied to a hive, and nothing reads the field. Objects
already in the store carry it and still decode, since unknown fields are
ignored; a test parses one. The controller stops writing it.

With the credential no longer naming a hive, and the agent's policy
naming none since #4762, nothing in the mint consumes one. `hive` goes
from `mint_and_verify`, from the `MintAgentIdentity` node, and from
`POST /api/agents/{name}/identity`, which now takes no body and no longer
checks a hive against the roster; a caller that still sends one is not
refused, the body is ignored. `swarmctl agent mint-identity` loses
`--hive`, so passing it is now a usage error.
2026-09-28 08:24:52 +02:00
atlas
d56d8f2b36 swarmctl: forge make-admin
Calls POST /api/forge/users/{name}/admin and prints what it found. It
fails with the controller's message when the user has not logged in via
SSO yet, and when the name is an agent's.

Refs #3782
2026-09-25 08:29:56 +02:00
atlas
dd32a395f7 agents: pull the forge token from bao; drop tea-login
forge-token.nix fetches swarm/agents/<agent>/forge-token under the
agent's own store identity into /run/hive-agent-forge-token/token, and
re-fetches on a timer so a rotation lands. hive-forge, the git
credential helper, hive-forge-notify, forge-avatar-sync and the web UI
read that file first and fall back to <state>/forge-token.

tea-login is deleted: it copied the token into ~/.config/tea, which
docs/swarm/credentials.md forbids for a store secret. hive-forge covers
the same verbs. swarmctl gains agent mint-forge-token.

Refs #3782
2026-09-24 17:48:53 +02:00
atlas
1442168715 swarmctl: re-mint an existing agent's store identity
Agent creation at swarm level is event-driven and nothing sweeps for
agents missing a credential, so an agent created before a credential
joined the mint never receives one -- nothing comes back around to it.
Without a way to re-run the mint by hand, the only route to giving an
existing agent its queue credential would be to delete and recreate the
agent.

POST /api/agents/{name}/identity enqueues the same MintAgentIdentity
node POST /api/agents declares, rather than writing inline: a second
code path that mints an identity is a second place for the four strings
that have to agree to disagree. swarmctl agent mint-identity is the
operator end, the same POST-and-print-the-node-id shape agent create
already has.

--hive is required on both ends. Neither the CLI nor the controller
keeps a roster of which agent runs where, and the credentials this mints
name a hive, so a default would be a guess that hands an agent subjects
on a hive it does not run on.

Documents the backfill as a runbook step, and fills in the renewal cell
the credential matrix requires for the new row.
2026-09-21 20:38:55 +02:00
atlas
30fa54cbc6 feat(swarmctl): add agent create, queueing the swarm-controller creation DAG
`swarmctl agent create <name> --hive <hive>` POSTs `/api/agents` to
swarm-controller over the daemon's unix socket and prints the queued
job's node id.

It deliberately does not wait. The endpoint queues a DAG whose last node
*publishes* a deploy message; the hive's `hive-c0re` then converges on
its own clock, out of the controller's sight. So even a fully settled
graph would not mean the agent is up, and there is nothing this CLI
could wait for that would let it claim otherwise. Printing the id is
exactly what the response says and all of what it says.

Transport is a bare hyper HTTP/1.1 client handshaked onto a tokio
`UnixStream` via `hyper_util::rt::TokioIo` — the same crate family
`hivectl/src/watch.rs` and `hive-agent/src/web_ui/proxy.rs` already use,
all of it already workspace-pinned. The request/response shapes are a
local mirror rather than a shared crate: the controller's own types are
private to its binary and this crate does not link it, the same
separation `hivectl` keeps from `hive-c0re`.

Errors are reduced to one actionable line — the controller answers
RFC 9457 problem+json, so an unknown `--hive` reaches the operator as
the roster of hives that would have worked rather than a body dump.
Response `warnings` are printed when non-empty.

The nix module wraps the binary with `SWARM_CONTROLLER_SOCKET`, read
from the same `socketPath` the daemon binds.

Refs #4399
2026-09-14 19:40:23 +02:00