Watch
0
0
Fork
You've already forked hyperhive
0
Commit graph hyperhive/swarm-queue-client/src
Author SHA1 Message Date
damocles
cc7b298cf3 swarm-queue-client: never rotate the queue token more often than a floor, and not at all for a token too short to rotate 2026-11-15 19:39:50 +01:00
damocles
43e7a3a6cb swarm-queue-client: mint the next token and reconnect before the queue expires the connection 2026-11-15 19:39:50 +01:00
damocles
c0b9c1778c hive-bash-mcp: publish each task's status and output to the swarm queue 2026-11-15 18:23:28 +01:00
iris
faadc5abe2 swarm-queue-client: no doc link to a crate-private function 2026-11-15 18:22:16 +01:00
iris
1a698f1a97 swarm: bash task status keys expire after 72 hours
Nothing deletes the key of a finished task, so the bucket would grow with every
task an agent runs. The shared bucket helper takes an optional max age, applied
when the bucket is created; a live task's republishing keeps its own key.
2026-11-15 18:22:16 +01:00
iris
1779fa0568 swarm: the contract for an agent's bash task status and output
A status key per task in the bash-task-status bucket and the task's output rows
in a per-agent stream, kept apart as for subagents. The controller creates the
bucket and the streams, and the agent credential may publish its own keys and
subjects. Nothing publishes yet; the bash MCP half follows.
2026-11-15 18:22:16 +01:00
iris
c7d7f0df55 swarm: subagent-status KV bucket, the run state of each subagent
One key per subagent (<agent>.<subagent>) holding running/finished, since,
exit, model, effort and the publish time. The controller creates the bucket,
and the agent credential may publish its own keys. Nothing reads or writes
it yet; the daemon publisher and the controller list follow.
2026-11-15 15:36:12 +01:00
atlas
13ff33ead0 archetype flows on the board list the agents the router sends to, and the docs say a switch-off key outlives its flow 2026-11-11 21:40:17 +01:00
atlas
68c24643f6 archetype flows behave like the built-ins: switch off per archetype or per agent, board cards, duplicate
The document's disabled list takes archetype:<archetype>/<flow> (off for every member) and the same with @<agent> (off for one member); a key naming nothing is ignored. GET /api/notify/flows lists the archetype flows with their members, the board draws a card for each with a switch, a checkbox per agent and duplicate, and the dry run counts their hits. The flows keep following the archetype.
2026-11-11 21:40:17 +01:00
atlas
1efbb72fdd an archetype carries its own notification-router flows for its agents
ArchetypeMeta gets flows, checked when the controller stores the record: every todo must go to the new members recipient, which the router expands to the agents of the archetype. The operator's flows document refuses members. The router reads the archetype records and each roster agent's archetype key, and its NATS grant gains read on both buckets.
2026-11-11 21:40:17 +01:00
iris
53224d877e swarm ui: the create form picks an archetype from tiles with their icons
An archetype's meta gets an optional icon, the SVG text (at most 16 KiB), which
the controller checks, stores and serves on GET /api/archetypes. The built-in
archetypes read theirs from nix/archetypes/icons. The create form shows each
archetype as a tile with its icon and name, laid out like the broadcast screen's
agent tiles, in place of the dropdown, and draws the SVG through an img data URL.
2026-11-11 10:09:50 +01:00
atlas
a5470d82ae an archetype's prompt prefills the new agent's agents.md in the create form 2026-11-11 02:16:22 +01:00
atlas
dd5fde848f squid blocks a runtime deny list from the controller in every egress mode, ahead of the allow list 2026-11-04 22:16:35 +01:00
atlas
47c2acb640 deploy the closure the swarm declares, read no forge head, drop the update badge 2026-11-02 07:55:10 +01:00
atlas
e46ad07226 add a per-agent egress log mode 2026-10-30 08:52:22 +01:00
atlas
c9abc2a42b make squid's allowed domains editable at runtime in the swarm ui 2026-10-28 23:01:43 +01:00
atlas
3493ba4a96 count suspended time when ageing the queue client's cached token
The cache aged the token on Instant, which does not advance while the host
is suspended, so a resumed host kept presenting an expired token. Age it on
the wall clock, and drop the token when the queue refuses it.
2026-10-28 08:49:55 +01:00
atlas
a55d55e494 read the cache facts on the hive and let hives read the closure bucket
hive-c0re reads its cache facts from the store every five minutes and writes /run/hyperhive/attic/{netrc,substituter,public-key}, each 0600. it accepts only an https url, so the pull token never travels in clear. hives get a read grant on the agent-closures bucket, and swarm-queue-client a reader for it. part 2 of 3: nothing uses the files or the reader until the probe lands.
2026-10-28 01:19:56 +01:00
atlas
8699857024 swarm: unrestricted_internet flag on the agent permissions record, unset reads as on, create writes false 2026-10-27 14:44:41 +01:00
atlas
5e64048e53 gate each deploy on a swarm build of the agent from internal/meta 2026-10-26 18:52:33 +01:00
atlas
2f00f9041f agent permissions: drop hive-c0re's swarm-agent-permissions.json fallback, fail closed before the first read
Removes the HiveCopy hand-off: hive-c0re no longer writes each agent's
entry from swarm-agent-permissions.json into its runtime dir as
swarm-permissions.json, and the harness and subagent daemon no longer
read it. hive-c0re keeps the file as its own last read of the swarm for
the manage_root_agent mounts. Also removes permissions_import::pending(),
its only caller being the hand-off. No nix provisioned the copy, so no
nix option is removed and no tombstone is needed.

Rule after this change: until an agent's first successful read of its
key it holds no permissions (no execution, so no bash), and a subagent it
starts holds the same. After a read a set key wins, [] holds none and an
unset key is the default. A failed read keeps the last value, so an
unreachable queue at start keeps the agent closed until a read succeeds.
The harness logs once at warn that it starts closed.

Fixes #5315
2026-10-25 02:17:32 +02:00
atlas
0f7b403803 agents get a todo when assigned to an issue or PR, rewritten when unassigned and cleared on close
A new swarm service, swarm-notify-router, in its own container on the host
that sets deploy.notify-router.enable (defaulted on by allSwarmServices where
the host has the router's store leaf, like the claude login minter).

- swarm-controller registers instance-wide hooks for `issues` +
  `issue_assign` and `pull_request` + `pull_request_assign`, updates the
  event list of a hook already registered with fewer (leaving its active flag as it is), and keeps each
  verified body unchanged in the SWARM_EVENTS stream on
  $SWARM.events.forge.<X-Forgejo-Event-Type>, deduped by X-Forgejo-Delivery.
  A delivery the stream did not keep answers 503.
- swarm-notify-router drains its durable consumer on that stream, runs its
  flows (source -> match -> todo sink, stored as nodes plus edges and checked
  to be a tree) and queues each op on $SWARM.router.todo.<agent> in the
  work-queue stream ROUTER_TODOS. Built-in flows: assigned, unassigned (same
  key, rewritten) and closed (prefix-clears every holder's router todos). A
  per-issue snapshot in its notify-router-state bucket holds the last
  assignees and the todo holders; only agents in the agent-status bucket get
  ops, and an assignee that was not one is left out of the snapshot so the
  next assign event retries it. After the first event that must be retried,
  the rest of the batch is nakked so redelivery keeps stream order.
- hive-agent creates its own durable consumer todo-<agent> and applies each
  op to its todo store as subsystem `router`, then acks; a refused op stops
  the rest of its batch the same way. The store's (subsystem, key) dedup
  makes a redelivery harmless. New Todos::clear_prefix.
- swarm-nats-auth: a --notify-router-client arm; the controller may publish
  $SWARM.events.forge.*; each agent gets three {agent} templates (extended
  consumer create whose filter the server checks, MSG.NEXT, ACK).
- Credentials: a notify-router store leaf minted by glue-bao-tls, a
  swarm-notify-router bao policy (read on its own queue client secret), an
  authelia machine client, the secret publisher entry, a journal forwarder
  route. No manual step.

hive-forge-notify is unchanged, so agents also still get forge's own
notifications.

Refs #5571
2026-10-22 07:28:53 +02:00
atlas
0831b85b73 swarmctl shares the create-agent, inference and permissions shapes with the controller 2026-10-19 18:58:00 +02:00
atlas
839efd1642 agent_permissions, agent_term and subagent_term take their wire contracts from swarm-wire 2026-10-19 18:30:44 +02:00
atlas
fc72bea151 swarm-wire: take notices's wire types, fmt the moved imports 2026-10-18 22:50:02 +02:00
atlas
e111807f59 swarm-wire: take agent_schedules's wire types 2026-10-18 22:50:02 +02:00
atlas
9cce9c470f swarm-wire: take agent_limits's wire types 2026-10-18 22:50:02 +02:00
atlas
266be0a5db swarm-wire: take wanted's wire types 2026-10-18 22:50:02 +02:00
atlas
c6b1364684 swarm-wire: take agent_icon's wire types 2026-10-18 22:50:02 +02:00
atlas
9222a051f5 swarm-wire: take agent_status's wire types 2026-10-18 22:50:02 +02:00
atlas
e4a9005468 move Permission into hive-types, drop swarm-controller's hive-sh4re dep 2026-10-18 21:40:01 +02:00
atlas
6ea13c02e3 move the review classifier, agent login and inference types into swarm-wire 2026-10-18 19:27:30 +02:00
atlas
87b1f94ac6 move agent_token from swarm-queue-client to swarm-agent-verify
per #5471, verifiers stop depending on the queue client. the module is
store-free and needs only thiserror, so swarm-agent-verify gains one
dependency and loses its swarm-queue-client edge. consumers import it
from swarm_agent_verify; swarm-logs and swarm-metrics, whose only use of
swarm-queue-client was this module, drop that dependency, as does
swarm-logs-auth.
2026-10-18 16:58:39 +02:00
atlas
741fe388f9 move startup_read from swarm-queue-client to swarm-secret-client
read_with_startup_retry retries the first read of a client secret through a store that is restarting. Nothing in swarm-queue-client calls it; its only callers are the four binaries that read the secret (swarm-builder, swarm-claude-login, swarm-controller, swarm-review-classifier), and they already depend on swarm-secret-client. It sits beside stored_secret, which moved there in #5538. swarm-queue-client drops its backon dependency and the tokio test-util dev-dependency that only these tests used.
2026-10-18 16:11:55 +02:00
atlas
456cf9a7cb set an agent's initial agents.md at create 2026-10-18 15:54:14 +02:00
atlas
0c3d128de1 move the builder and claude login wire contracts into swarm-wire 2026-10-18 12:36:45 +02:00
atlas
88674eb63a read an agent's agents.md through the swarm and edit it with git diffs
The agent's state/AGENTS.md stays the truth and its harness the only
writer. The harness mirrors the file into a new `agents-md` KV bucket,
and swarm-controller serves that copy, so a read works while the agent is
stopped. An edit is a unified diff plus the revision it was made against,
sent over the agent's existing control subject; the harness refuses a
stale base, applies the diff with `git apply --check` then `git apply` to
a temp copy, and writes the file only when both pass. Fixes #5519.

Files:
- swarm-queue-client/src/agents_md.rs, lib.rs: the bucket name and
  subject, the mirror value, the `ApplyAgentsMd` control command and its
  reply. `AgentControl` loses `Copy` because the new variant carries text.
- hive-agent/src/swarm_agents_md.rs, main.rs: the mirror task (start,
  10 s re-read, immediate republish after an edit) and the apply.
- hive-agent/src/swarm_input.rs: the control loop answers both commands.
- hive-agent/src/claude_md_watch.rs: its AGENTS.md path is shared.
- hive-agent/Cargo.toml, Cargo.lock: sha1 for the git blob id (already in
  the lock), tempfile moves from dev to runtime for the apply directory.
- swarm-controller/src/agents_md.rs, main.rs: the bucket reader and
  GET/PUT /api/agents/{name}/agents-md; test AppState literals get the
  new field.
- swarm-controller/src/agent_control.rs: the sink hands back raw reply
  bytes, so each command decodes its own reply type.
- swarm-controller-api/src/agents_md.rs: the route bodies, shared with
  swarmctl.
- swarmctl/src/agents_md.rs, main.rs: `swarmctl agent agents-md get|apply`.
- nix/host-modules/swarm-nats.nix, nix/module-eval/nats-authelia.nix: the
  agent's publish grant on its own `$KV.agents-md.<agent>` key.

git needs no new package: agent containers enable programs.git, and the
harness unit's PATH includes /run/current-system/sw.
2026-10-18 12:35:11 +02:00
atlas
f4f459f0b2 move stored_secret from swarm-queue-client to swarm-secret-client
The module is a rename (97% similar); everything else in the diff follows
from the move:

- stored_secret.rs: private `chain()` helper, the 8-line copy of the queue
  crate's `chain` the module called as `crate::chain`; the queue crate's
  public `chain` stays.
- stored_secret.rs: `Duration::from_mins(5)` in `MAX_REREAD_DELAY` is
  unchanged moved code, not a new use.
- swarm-secret-client Cargo.toml: gains futures-util, tokio and tracing as
  dependencies and tokio `test-util` plus tracing-subscriber as dev
  dependencies; the module and its tests use them.
- swarm-queue-client Cargo.toml: drops futures-util and the tracing-subscriber
  dev dependency, which only the moved module used; its tokio dev comment is
  reworded to the tests that remain.
- Cargo.lock: the same dependency moves, nothing else.
- swarm-queue-client lib.rs: imports `swarm_secret_client::stored_secret`,
  and its tests use a small local `Store` in place of the moved module's
  test-only `Fake`, which is no longer reachable.
- callers (hive-c0re, swarm-builder, swarm-ci-metrics, swarm-claude-login,
  swarm-controller, swarm-nats-auth, swarm-review-classifier): import path
  only; no re-export is left behind.
2026-10-18 09:00:08 +02:00
atlas
44984e0d36 move install_crypto_provider to swarm-secret-client
The rustls provider install is process setup every swarm binary runs, and
swarm-secret-client depends on no other swarm crate, so a binary that only
needs the provider and the store no longer links the queue client.

- swarm-secret-client/src/lib.rs, Cargo.toml: the function, its test, a
  `//!` line on why it lives there, and the rustls dependency; rustls
  `ring` as a dev-dependency feature, so the test builds both providers as
  the binaries do and fails if the install is dropped.
- swarm-queue-client/src/lib.rs, Cargo.toml: function and test removed;
  `connect()` calls the secret client's; rustls dropped (no other use),
  swarm-secret-client added.
- swarm-metrics-auth/Cargo.toml: drop swarm-queue-client, whose only use
  was the provider; the binary no longer links async-nats.
- swarm-logs-auth, swarm-logs, swarm-metrics, swarm-ci-metrics Cargo.toml:
  the dep comment no longer names the provider (they keep the dep for
  agent_token / the OIDC mint).
- Cargo.toml: the rustls comment names the provider's new home.
- 14 call sites (hive-agent, hive-c0re, hive-subagent-mcp x2,
  swarm-builder, swarm-ci-metrics, swarm-claude-login, swarm-controller,
  swarm-logs-auth, swarm-logs, swarm-metrics-auth, swarm-metrics,
  swarm-nats-auth, swarm-review-classifier): one-line path change.
- Cargo.lock: the dependency edges above.
2026-10-18 03:18:43 +02:00
atlas
d9c586ef94 swarm-controller: replay a subagent's newest rows before its live tail
A subagent's terminal opened directly showed nothing until the subagent
published again: the relay was a core subscription, though its rows sit
in term-sub-<agent> for 24 hours. It now goes through the agent route's
replay-then-tail, so it opens with the subagent's newest 100 rows and
resumes after Last-Event-ID.

- term_stream.rs: the replay takes a stream name and a filter subject
  instead of an agent, and `replay_or_live` (replay, else the live core
  subscription) is the one fn both routes call. When the filter is one of
  several subjects in the stream, a fresh reader's consumer starts at the
  oldest row and drops all but the newest REPLAY_ROWS it counts as
  pending, since the stream's newest sequences may belong to sibling
  subagents. A single-subject stream (term-main-<agent>) picks its start
  with replay_start exactly as before. Tests for both subagent cases.
- subagent_term.rs: the relay calls replay_or_live on the subagent's
  subject in term-sub-<agent> and reads Last-Event-ID; module doc and
  OpenAPI text no longer say "live only".
- main.rs: the handler test passes the new headers argument.
- swarm-queue-client agent_term.rs: replay_consumer_config takes the
  filter subject instead of the agent.
- swarm-queue-client subagent_term.rs: consumer_create_grant, the
  extended CONSUMER.CREATE form with a subagent subject as filter.
- swarm-nats-auth policy.rs: the controller holds that grant; the agent
  grant's `$SWARM.term.*` filter is two tokens short of a subagent
  subject, and the test that refused it now admits it.
- docs/swarm/ui.md, README.md: a subagent terminal opens with its newest
  rows.

Fixes #5527
2026-10-18 03:00:08 +02:00
atlas
bf848ba65f swarm-builder: refuse a build that waited too long for the slot
A request that waits behind another build, e.g. one a controller
restart orphaned, could start a full build after most of the
controller's 61-minute reply deadline was gone. The builder now waits
at most REPLY_DEADLINE - BUILD_DEADLINE - 30 s for the slot and
answers Failed, without building, past that. The 30 s covers the
forge token read, now bounded to 20 s, and the reply's trip back.

Fixes #5437
2026-10-17 23:30:13 +02:00
atlas
e405c2b098 swarm-nats-auth: give issued user JWTs an exp
The user JWT the callout issues carried no exp, so nats-server armed no
expiry timer and a connection kept its grant until the client dropped it:
a deleted or re-minted agent queue secret, a removed OIDC client or an
expired access token never reached a live connection.

Every issued user JWT now expires at the presented OIDC token's exp
(read from the introspection answer) or an hour after issue, whichever
is sooner; the agent queue-secret path has no exp of its own and gets
the hour. Every queue client reconnects through an auth callback that
re-reads or re-mints its credential, so the forced reconnect presents a
current one.

Fixes #5432
2026-10-17 16:29:55 +02:00
atlas
b199c83899 archetypes: create an agent from an archetype with no config repo
POST /api/agents takes an archetype the stored list holds, records it in
the new agent-archetype bucket, and queues no CreateRepo, AddRepoMember or
InitAgentConfigRepo for it. A destroy clears the key. Hives may read the
bucket.

Refs #5386
2026-10-17 16:27:21 +02:00
atlas
49015eb553 keep the operator list under one key and read it with one direct get
The agent read the operator-profiles bucket with a keys() listing, an
ordered consumer whose grant (STREAM.INFO, CONSUMER.CREATE) and flow-control
reply had never run against a permissioned queue. The controller now writes
the whole list as one JSON array under one key, only when it changed, and
the agent reads it with one DIRECT.GET the way agent_inference::read_own
reads its key. The agent grant shrinks to that one subject.
2026-10-17 13:10:05 +02:00
atlas
6942f8fd27 publish operators' forge-profile pronouns to kv and list them in the agent prompt
swarm-controller reads each member of authelia's operator group's forge
profile every five minutes and keeps one key per operator in the new
operator-profiles bucket. Every agent may list and read that bucket, and
hive-agent renders it into the system prompt at harness start, falling
back to today's prompt when the bucket is empty or doesn't answer within
five seconds.

Fixes #5383
2026-10-17 13:10:05 +02:00
atlas
51035eac1d 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
2026-10-17 12:57:20 +02:00
damocles
9a3153cb08 retry: share one backon helper for the client secret reads and convert the remaining doubling tables 2026-10-17 12:31:29 +02:00
atlas
7b5400ecb8 grant manage_root_agent by stored permission only, not by the name ruth
Permission::default_for and hive-sh4re's MANAGER_AGENT are gone: every
agent with no permissions key holds Permission::DEFAULT, ruth included.
The agent-name parameters that only fed default_for go with it
(permissions::effective, HiveCopy::before_read/after_read and their
callers).

swarm-controller seeds the grant ruth held by name: at start, a declared
ruth whose permissions are unset gets DEFAULT plus manage_root_agent
written into her key, once, logged. A set key is never touched. It is a
one-shot to delete once every swarm has passed it.

The swarm UI setup assistant opens the create form with
manage_root_agent checked, so a new ruth is created with the grant.
permissions_import no longer adds ruth to the import or splits her
default. Docs and help text no longer say ruth holds the grant by
default; swarmctl-cli.md regenerated.
2026-10-17 08:10:08 +02:00
atlas
b68a8ef290 swarm-authelia: run the secret publisher and a journal forwarder inside the container
swarm-secret-publish and its path unit now run inside authelia's container,
beside the mint, and read each client secret at the path authelia writes it.
The host unit and host path unit, which read the secrets through the
container's root on the host, are gone, and so is
deploy.authelia.hostClientSecretDir, which only they read. The publisher keeps
its store identity, the secret-publisher leaf. The host hands the leaf and the
store's CA into the container as nspawn system credentials, and the unit loads
them; where this host mints the leaf, the container waits for swarm-bao-pki.
Authelia does not wait for the publisher, which retries every 30 seconds while
the store is down.

The container also runs the journal forwarder the other service containers
run, pushing to a receiver of its own on the swarm collector (serviceProducers
swarm-authelia) as a new machine OIDC client (swarm.authelia.otel.clientId)
that authelia registers for itself and the publisher carries to the store. The
fetch unit reads it back under the publisher's leaf, also handed to it as a
system credential, so the swarm-secret-publisher policy gains read on that one
leaf; its stanza keeps create and update, which the exact path would otherwise
take from the services/* prefix. Without the leaf the forwarder is off.

The container drops --link-journal=host, and the host collector stops reading
it. The services' "nothing at <path>" errors name the publisher in authelia's
container.
2026-10-17 00:28:16 +02:00
atlas
ce7b3e055f review-classifier: ask a check once more when its answer isn't valid JSON
An answer that fails to validate is asked for a second time, for that
classification only, with a reminder to reply with the JSON object alone.
The second ask comes out of the classification's CLASSIFICATION_DEADLINE:
it gets what the first ask left, and is skipped when less than 20 seconds
remain, so RUN_DEADLINE's bound still holds. A failed ask (runtime error,
timeout) is not repeated. If the second answer does not validate either,
the row has no answer, as before.

Model::ask takes the deadline for the ask; the ACP runtime no longer holds
its own.

The parser also takes the first fenced block of a reply that has prose
around the fence; prose around a bare object is still rejected.
2026-10-16 16:55:07 +02:00