A rebuild's MetaSync resolves the head of the swarm's internal/meta once and
records it; the prebuild and the swap build that flake instead of the hive's
own, and a first deploy does the same in Create. hive-priv takes the commit as
a validated meta_rev on UpdateContainer and CreateContainer and builds from its
own forge URL (HIVE_PRIV_FORGE_URL).
The head is used only when its flake lists the agent at the config commit the
forge's main is at; otherwise, and when the repo has no commit or this hive has
no core token file for nix's git, the hive renders locally as before.
hive-c0re's and hive-priv's gitconfig now share the forge credential helper and
rewrite the forge's https URLs to http, which hive-priv lacked.
The swarm controller offers its own running rev (services.hyperhive.c0re.
hyperhiveRev) as the hyperhive pin of internal/meta and takes it only when
the forge compare API shows it descends from the committed pin; merges to
hyperhive main no longer move the pin, and the first commit pins main's head.
When a commit moves the pin, every placed agent gets a TriggerDeploy.
A deploy after a meta update no longer waits on it succeeding (after_any), so
a down builder does not hold back hives that render locally.
A new update_meta node renders internal/meta's flake.nix with
hive-meta-flake, the renderer hive-c0re uses: hyperhive pinned to its
forge main, each custom agent a hive places pinned to its agent-configs
main, all over https, and the swarm-wide values from
SWARM_CONTROLLER_META_FLAKE (set by swarm-controller.nix from
services.hyperhive.swarm.*). The swarm builder locks it, and the
controller commits flake.nix and flake.lock in one commit and gives
every hive's forge account read on the repo. A render equal to the
committed flake.nix sends no lock and commits nothing.
It runs at start, when hyperhive's main moves, before the deploy of a
merged config PR and before the first deploy of a new agent; those
deploys now wait for it. agent_web_port moves into hive-meta-flake so
both callers compute the same port; hive-c0re re-exports it.
On deploy: internal/meta appears on the forge and follows every merge.
No hive builds from it yet.
Refs #4857
HIVE_AGENT_NATS_URL now reaches each container in its hive facts, from
the host's deploy.hive-controller.queue.agentNatsUrl, instead of being
rendered into the meta flake. The agent option
services.hyperhive.agent.queue.natsUrl is removed; the harness unit
imports the queue credential whenever the token endpoint is set.
On deploy every agent rebuilds once (the rendered flake loses the
address), and the harness reads the address from the facts file
hive-priv writes before each start.
Refs #4857
hive-c0re's render_flake and the pieces it needs (AgentSpec, AgentPin,
pinned_git_url, the forwarded-var tables, canonical_inputs_in_lock) move
into a new crate with no IO. The forwarded values and the GitHub switch
are now arguments; hive-c0re passes what it read from its own unit env,
as before. Nothing changes on deploy: the rendered flake is byte-for-byte
the same, and hive-c0re's render tests pass unchanged.
Refs #4857
A request on $SWARM.builder.lock-meta carries a rendered flake.nix. The
builder writes it into a scratch directory, runs `nix flake lock` there
with the same forge credential helper its builds use, answers with the
flake.lock nix wrote, and removes the directory. It commits nothing; the
controller commits the lock beside the flake.
Locks are answered in their own loop, like archetype evaluations, so one
does not wait behind an hour-long build. The auth callout grants the
subject to the builder (subscribe) and the controller (publish) only.
Ported from c574a1694 onto the swarm-wire crate.
Refs #4857
On deploy: a review requested of the review classifier on a PR whose repo
holds neither .hyperhive/review-classifier.yml nor .json on its default
branch now posts an APPROVED review at once, saying no review rules are
configured for the repo and it was approved without checks. No model call
is made. Before, that case posted a "No verdict" comment. A rules file that
exists but fails to load or parse still posts no verdict. The rule model is
unchanged and instance-wide: every repo without a rules file is approved.
The router gets a request_reviewer node and a pull_request_sync source;
the built-in head-classifier flow asks swarm-controller, over
$SWARM.router.sink.request-reviewer, to request review-classifier on
every push, unless its review of the head already stands. The controller
subscribes to pull_request_sync on its pull-request hook and answers
the router; nats-auth grants the router the publish and the controller
the subscribe.
The flow editor can now change a flow's shape on the canvas, not only
through the inspector's fields:
- drag a box onto another to move it and its branch there; a drop the
save would refuse shows the target dashed red and gives the reason;
- a palette of node kinds adds a node below the picked box on click, or
below any box it is dragged onto; it offers what the controller lists;
- arrow keys walk the tree, M picks a box up and Enter drops it under
the focused one, Alt+Up/Down reorder a node among its siblings, Delete
removes a branch; focus follows the edit;
- each box with siblings says when it runs, and the inspector can move
it earlier or later, since the router runs children in edge order;
- request_reviewer is a second leaf kind beside todo, with a reviewer
field;
- leaving with unsaved edits asks first, also for in-app links and the
back button (a router hook runs navigation past a page's guard).
authelia.agentClientSuffix is removed (tombstoned) and the responder
takes no --agent-client-suffix: hive-<name>-agent is parsed as hive
<name>-agent like any other id. The reserved word agent still keeps a
hive from being named *-agent.
A custom flow opens at /notifications/<id>: its nodes drawn left to right
(source, then matches, then todos), and a panel to edit the picked node: the
source's events, a match's conditions, a todo's recipients, op, key and text.
Add a match or todo below a node, move a branch under another match, or remove
it; an edit the controller would refuse (a todo with children, a second
parent, a cycle, over the node or depth cap) is turned away with the reason.
Edits stay in a draft until save, which sends the whole document at the
revision read. A stale revision is told apart from a refused document: the
editor offers to reload or to keep the draft on top of the latest. The form
choices come from the controller's schema when it sends one.
The list page gains edit and duplicate on custom flows, duplicate on the
built-ins, and a new-flow box; a new flow's id avoids the built-ins' too.
Every agent now connects to the queue and reads logs with its own secret
from the store (#5181), so the per-hive `hive-<h>-agent` OIDC client has
no reader left. Remove it end to end:
- hive-c0re no longer forwards `hive-queue-agent-{secret,client-id}` into
containers, and no longer forwards HIVE_AGENT_OIDC_TOKEN_ENDPOINT; the
agent module that loaded them (nix/agent-modules/queue.nix) is gone, its
options refused with a removal message.
- The host reader (glue-queue-agent-credential.nix, swarm-bao-queue-agent),
its store role and policy unit (swarm-bao-queue-agent-policy), its PKI
leaf and the deploy.bao.queueAgent* / queue.agentCredentialDir options
are gone; the options are refused with a removal message.
- authelia mints no `hive-<h>-agent` client, and swarm-secret-publish no
longer writes swarm/hives/<h>/queue/agent.
- swarm-secret-client drops queue::agent_client_path / queue::Credential.
- HIVE_AGENT_NATS_URL is now gated on the agents' queue address alone.
The log store's authelia check stays for operators. swarm-nats-auth still
refuses a `-agent` client id; that refusal and `agentClientSuffix` are left
for a follow-up.
Refs #4459
Refs #4765
Pure edits on a flow (add a child, remove a branch, move a branch, replace a
node, new and duplicate flow) that refuse what the controller's tree rules
would, plus the save request and what its status means. Nothing uses them
yet: the editor page that does comes next.
A request whose head the PR has moved on from was dropped without a word. It now runs on the current head, as before the queue. The re-post shortcut, keyed on the request's head, applies only while the PR is still at that head, so a moved PR never re-posts an old head's verdict.
take and consume carried the forge, the classifier queue, the review poster and the run store as four separate parameters, which took take to eight and needed a too_many_arguments expectation. They travel together everywhere they are used, so they are now one Handles value, cloned per taken request. take has five parameters and no lint attribute. No behaviour change.
- table cells: cut to what the credential is, where it comes from and what the operator does
- prose sections: cut the matrix-prefix, appservice-token and leaf-delivery sections; trimmed revocation and retry notes to their operator-visible effect
- progressive enhancement section: removed (contributor rules)
- intro: cut to the store, the on-disk certificate and the column meanings
- docs index: drop the "progressive-enhancement rule" clause from the credentials.md entry in docs/README.md
- matrix rows: one sentence saying the name `main` is reserved
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
The timeout kill was sound; the test's liveness probe was not. is_dead
read /proc/<pid>/stat and counted only state Z as dead. While init reaps
a zombie, the task shows state X (EXIT_DEAD) for a few microseconds
before its /proc entry goes away. When the poll loop's first probe saw Z
and the final assert's re-probe landed in that window and saw X, the
test failed at once with "grandchild <pid> outlived the timeout". CI run
#6059 shows this: the hive-priv binary finished in 2.00s, so the 5s poll
never ran.
is_dead now counts X as well, so it is monotone (running, Z, X, gone)
and the re-probe can no longer flip it back to alive. A unit test pins
the Z/X/S/R parsing.
Stress loop: the test binary filtered to this test, 2000 runs, 32 in
parallel, with 8 busy-loop CPU hogs. Before: 4/2000 failed, all with the
CI signature (finished in ~2.01s). After: 0/2000.
When the server answers an event stream with an error status the browser
gives up and the badge stayed on disconnected with nothing to press. The
terminal, its state header and the messages page now offer a reconnect,
and the one rule for the state after an error moved into a tested helper.
The resource caps panel gets a retry beside its error, and its loading
state announces itself.
A set HYPERHIVE_CLAUDE_CODE_PATH whose build is gone (collected after a
bump while the container still holds the old facts) made the wrapper
run the container's own claude-code silently, which can be a different
version. It now prints one line naming the missing path.
Refs #4857
The three values that differ per host (deploy.hive-controller.dashboardPort,
claudeCodePackage and otel.extraResourceAttributes) no longer go into the
meta flake. hive-priv writes them into the container's
/etc/hyperhive-hive-facts with the hive name and domain, and the agent
reads them at boot:
- HIVE_DASHBOARD_PORT reaches every unit through the hive-facts generator;
the web UI still defaults to 7000 without it.
- `claude` on PATH is a wrapper that runs HYPERHIVE_CLAUDE_CODE_PATH's
claude when it is there, and the container's own claude-code otherwise.
The host's serve.json keeps gc-rooting the pinned build.
- OTEL_RESOURCE_ATTRIBUTES moves out of the units' Environment= into a
boot-time generator, which appends HYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES.
The agent options services.hyperhive.agent.claudeCodePath (now a removed
option) and otel.extraResourceAttributes go; only the meta render set them.
render_flake takes no HiveEnv any more, so the memory-cap render test and
render_hive_flake_at, which only existed to vary HiveEnv into the render,
go with it.
Refs #4857