Per operator review (#1899): the hive's own domain should never be
typed by hand. Remove the --domain flag from peer-config and wg init
entirely; both now resolve it from the daemon (HostRequest::HiveDomain).
peer-config errors with a clear hint if the daemon can't answer; wg init
prints the peer block best-effort (skips it, still enables the mesh, if
unresolved). Regenerated docs/tools/hivectl-cli.md.
Per operator review: hivectl shouldn't make the operator retype this
hive's own domain. Add a HostRequest::HiveDomain admin-socket query
(c0re answers from HYPERHIVE_HIVE_DOMAIN, already in its service env) and
a domain field on HostResponse.
hivectl peer-config now resolves the domain as: --domain override (kept
for offline/scripted use), else the daemon query; errors with a clear
hint when neither resolves. wg init resolves it best-effort too, so it
prints the hand-over peer-config block without --domain (skipped, not
failed, when the daemon is unreachable).
Regenerated docs/tools/hivectl-cli.md.
Add `hivectl peer-config --domain <this-hive-domain>`: prints the
`services.hyperhive.swarm.peers."<domain>"` nix block a peer operator
pastes to federate with this hive. Emits `caCert = ./<hive>-ca.pem` plus
a `cp /var/lib/hive-tls/ca.pem ./<hive>-ca.pem` line when this hive
serves a self-signed CA (the cert file exists); omits caCert for
ACME/public-CA hives (trusted by the default bundle). Includes the
WireGuard public key when the mesh key exists, and the
wireguardAddress/Endpoint passed via flags.
`wg init` gains an optional --domain; when set it calls peer-config at
the end, so a fresh mesh setup prints the hand-over block in one command.
Pure output — reads local state (TLS CA cert presence, wg key), never
mutates. Regenerated docs/tools/hivectl-cli.md.
Closes#1897.
Group issue/PR operations under `pr` and `issue` parent commands
(`hive-forge pr close 42`, `issue create …`, `pr status --pr 42`) per the
operator decision — kind-namespaced verbs replace the flat surface.
- new `verbs::pr_cmd` / `verbs::issue_cmd` parent commands wrap the existing
per-verb modules (reuse their Args + run fns) under `#[command(subcommand)]`.
- kind-validation (the win over the old generic verbs): the generics that work
on both (view/comment/comments/close/labels/assign/timeline) call
`assert_kind` first, so `pr close <issue>` / `issue close <pr>` are rejected
with a 'use the other command' message. PR-only / issue-only verbs are
kind-correct by construction. `number` exposed `pub(crate)` on the shared
verbs so the wrappers can probe it.
- every flat kind verb (`close`, `pr-create`, `pr-status`, `issue-edit`, …)
kept as a `#[command(hide = true)]` back-compat alias — still parses, dropped
from --help; removed in a later sweep once usage migrates. (`pr`/`issue`
bare-show become `pr show` / `issue show` — the names are now parents.)
- docs/tools/forge.md documents the new surface + the deprecated aliases.
cargo build/clippy/fmt clean, 54 tests pass; --help surface + alias parsing
smoke-tested.
New agents get a btrfs subvolume state root automatically when the host
FS is btrfs, but agents that predate that migration are left on plain
dirs and miss the subvolume feature set (snapshots, per-subvol
usage/quota, send/receive migration). Add an opt-in operator verb to
convert an existing plain-dir agent in place.
btrfs cannot promote a directory to a subvolume in place, so the new
privileged op stages a sibling subvolume mirroring the dir (create +
`cp -a --reflink=auto` preserving ownership/permissions/xattrs + match
the root's owner and mode), then atomically renames the original aside
and the subvolume into place, then removes the original. Any failure
before the swap leaves the original untouched; idempotent (no-op if
already a subvolume) and btrfs-gated.
The `hivectl subvol upgrade <agent> --yes` verb composes it client-side
like `restart`: stop the agent so its state bind-mount is released, run
the migration via hive-priv, then restart it — the restart is attempted
regardless of the migration outcome so a failed migration never leaves
the agent down.
- hive-sh4re: UpgradeAgentSubvolume priv request variant.
- hive-priv: the migration handler plus stage/cleanup helpers.
- hive-c0re: priv_client wrapper and the hivectl verb; regen CLI docs.
Follow-up to the btrfs-subvolume migration. Operator-opt-in disk
accounting + quotas on agent state subvolumes via btrfs qgroups:
- three privileged ops (qgroup ops need root): EnsureBtrfsQuota
(btrfs quota enable on the agent-state filesystem — statfs-gated,
idempotent, no-op off btrfs), ReadSubvolumeUsage (btrfs qgroup show
-f --raw for one agent), SetSubvolumeQuota (btrfs qgroup limit, or
clear). Reuses the is_on_btrfs helper from the subvolume work.
- priv_client wrappers, incl parse_qgroup_usage -> (referenced,
exclusive) bytes.
- hivectl 'quota' subcommand: enable / show [agent] / limit <agent>
<size|none>, with a K/M/G/T size parser + human-readable output.
Quota is deliberately NOT auto-enabled: btrfs quota enable triggers a
full rescan that is I/O-heavy on a large filesystem, and the operator
should choose when to pay that. 'quota show' on a plain-dir agent (no
subvolume) reports no qgroup data rather than erroring.
One-time-setup convenience for the inter-hive WireGuard mesh
(services.hyperhive.swarm) so nobody has to remember the wg dance:
- hivectl wg init [--address X] — generate (if absent) the hive's
private key at /etc/wireguard/hive.key (0400, never clobbered),
derive + print the public key, and print the swarm.wireguard nix
snippet to enable the mesh.
- hivectl wg peer <domain> --pubkey --address [--endpoint] — print the
swarm.peers.<domain> nix snippet to add a remote hive.
- hivectl wg status — wrap wg show wg-hive.
Hybrid model per the design: the verb owns the imperative state (the
key file), the operator pastes the printed nix into host config (kept
in git) — nothing mutates declarative config behind their back.
hivectl-only (root host ops, like the gateway htpasswd verbs); no
priv/wire/c0re changes.
flake: wrap hivectl with wireguard-tools on PATH so wg resolves even
before the mesh config (which would otherwise pull it in) exists —
wg init is the first setup step. Add clippy.toml doc-valid-idents for
the WireGuard proper noun. Regenerate hivectl-cli.md.
Add a 'hivectl completions <shell>' subcommand (clap_complete) that
prints a completion script for bash/zsh/fish/elvish/powershell, generated
from hivectl's own clap command tree so it never drifts from the real
verbs/flags. The package build installs the bash/zsh/fish scripts via
installShellFiles, so an operator gets working completion automatically
once hivectl is on their profile with shell completion enabled.
Regenerated docs/tools/hivectl-cli.md for the new verb.
Drop the enumeration of what the flag does not apply to, per operator
review on the sibling graceful-stop change. Regenerate the CLI doc so
the markdown-docs self-diff check stays in sync.
`hivectl restart [scope]` cycles the scoped containers — composes the
existing stop + start daemon ops client-side (reusing the merged Stop/Start
wire ops + global --socket), so no new wire/c0re surface. Same scope model
as stop/start (--agents/--agent/--ci/--forge/--gateway/--matrix), and
--graceful on the stop half. If the stop phase reports a failure the start
phase is skipped so a half-stopped hive isn't blindly started over.
Regenerated docs/tools/hivectl-cli.md.
The host admin socket path was duplicated on every daemon-assisted verb
(agents restart/restart-all, stop, start). Hoist it to a single global arg
on the top-level Cli (`--socket`, default DEFAULT_HOST_SOCKET, accepted
before or after the subcommand) and thread cli.socket through dispatch.
Verbs that don't talk to the daemon ignore it. Pure CLI-ergonomics change;
no wire/behaviour change. Regenerated docs/tools/hivectl-cli.md.
The hivectl-docs flake check regenerates docs/tools/hivectl-cli.md from the
clap command tree and asserts it's committed up to date. Adding the stop/start
verbs changed the CLI, so the doc was stale and the check failed. Regenerate it.
Forgejo 15 has no REST endpoint to download an Actions artifact — the
only path is the web UI download route, which is keyed by the run's
global id rather than the per-repo run number shown in run-page URLs.
The REST artifacts list route keys off the run number instead, so the
two can't be chained directly.
artifact-get takes the run number (what pr-status surfaces as a CI
context target_url), translates it to the global run id via the REST
runs list by matching each run's html_url tail, then GETs the web
download route with the agent's forge token. Saves the artifact zip to
a path (default /tmp/forge-artifact-<name>.zip) or streams to stdout
with -o -.
The artifact name is percent-encoded into the path. The encoder that
list already used for query-string filters is promoted to a shared
verbs::pct_encode helper so both call sites stay in sync.
Lets an agent pull a CI-built artifact (e.g. a paper PDF) into /shared
without host access.
Adds `hive-forge pr-merge <n> [--method merge|rebase] [--keep-branch]
[--force]` wrapping POST /repos/{owner}/{repo}/pulls/{n}/merge, so agents on
the peer-review-and-merge workflow have a CLI path instead of the raw API.
- Methods: merge (default) | rebase. Squash is intentionally not offered.
- Deletes the head branch after merge unless --keep-branch.
- Safe by default: refuses unless the PR is mergeable, CI is not red, and no
review's current verdict requests changes (latest-per-reviewer wins, so a
later approval clears an earlier request-changes). --force overrides and
also sets Forgejo's force_merge.
- New client helper post_no_content for the 200-empty-body merge response.
The daemon does not call join_room_by_id. It fires a wake and writes
to mcp-loose-ends/matrix.json; the agent calls list_invites + join_room.
Replace the inaccurate 'Auto-accept invites' paragraph with 'Invite
wakes' describing the actual flow. Merge the pending-invites paragraph
into a single accurate block.
The matrix wake body description said '[matrix] <sender> in <room>:
<first-100c>...' (old single-line teaser). After feat(#1137) it's now
a richer format:
- Single room, 1 message: terse one-liner with sender + body
- Single room, N messages: count + room name
- Multi-room: bulleted list with per-room summaries
Also note that the same breakdown appears in get_loose_ends via the
UnreadMatrix entry.
hivectl has verbs scattered across CLAUDE.md and domain docs (gateway.md
has the gateway subcommands, matrix.md has a brief mention) but no
single cheatsheet. Add docs/tools/hivectl.md covering all subcommands:
- forge create-user: provision/refresh agent or human forge accounts
- matrix create-user / sync-admin / promote-user / reset-password
- gateway create-user / delete-user / list-users (htpasswd management)
- agents restart / restart-all (container lifecycle via admin socket)
- choom <name> [--fresh]: interactive claude session in agent container
Also add a reading-path entry to CLAUDE.md pointing at the new file.