The hive applies one `agentCpuQuota` / `agentMemoryMax` to every
container. That's the right default and the wrong ceiling: a build-heavy
agent needs headroom the other twelve don't, and raising the hive-wide
value to suit it hands that headroom to everyone.
Adds a per-agent override, persisted host-side and resolved per-field
against the hive defaults.
Follows the existing `meta/*.json` pattern (`capabilities.json`,
`tool-groups.json`): a host-side map read by `hive-c0re`, staged and
committed in the meta repo so every change lands in the audit trail.
```json
{ "sock": { "cpu_quota": "400%", "memory_max": "8G" } }
```
Fallback is **per field**, not per agent: an entry with only
`memory_max` leaves that agent on the hive-wide CPU quota. Absent file,
absent agent and absent field all resolve to the hive default, so the
feature is inert until someone opts an agent in.
Unlike the other meta files this one is **not** injected into the
container — a limit is something done *to* an agent, not something it
reads about itself.
```
hivectl agents set-limits sock --cpu-quota 400% --memory-max 8G
hivectl agents set-limits sock --reset
```
Values are validated before they're persisted: they go into a systemd
drop-in verbatim, and a typo there makes the unit fail to *start* —
turning a fat-fingered quota into a container that won't come back.
The command is declarative: each call replaces the agent's whole entry.
That makes a forgotten flag a silent revert, so a bare `set-limits
<name>` is rejected at the clap layer and clearing needs an explicit
`--reset`.
`ContainerView` gains `cpu_quota` / `memory_max`, both always populated:
there's no "unset" state to render, only "same as everyone else". They
reflect what the drop-in *says* — what the next start will enforce — not
a live cgroup reading.
The write goes through `meta::commit_resource_limits` rather than the
bare setter, so it's staged and committed under `META_LOCK`. Writing
without committing would leave the meta working tree dirty for the next
`prepare_deploy` to trip over.
Docs: `persistence.md` (the new meta file, and why it isn't injected),
`tools/hivectl.md` (the prose guide), `tools/hivectl-cli.md`
(regenerated clap dump).
Closes: internal/requests issue 25
279 lines
14 KiB
Markdown
279 lines
14 KiB
Markdown
# hivectl
|
|
|
|
`hivectl` is the operator-facing host CLI for hyperhive. It lives on the
|
|
host (not inside any container). It talks to `hive-c0re` over the host
|
|
admin socket `/run/hyperhive/host.sock`, which is root-only by default —
|
|
so it needs `sudo` unless you grant sudoless access by listing your
|
|
login user in `services.hyperhive.c0re.adminUsers` (adds you to the
|
|
`hive-admin` group that owns the socket; see
|
|
[`docs/boundary.md`](../boundary.md#host-admin-socket-access-hivectl)).
|
|
Available via the `hive-c0re` package in the host NixOS config.
|
|
|
|
Unlike the `hive-c0re` daemon subcommands (which go through the broker),
|
|
`hivectl` covers direct host-side administration: manual provisioning of
|
|
forge + matrix accounts, gateway htpasswd management, container
|
|
lifecycle shortcuts, and interactive agent shell access.
|
|
|
|
This page is the curated guide. For the exhaustive flag-by-flag
|
|
reference auto-generated from the binary's own command tree, see
|
|
[`hivectl-cli.md`](hivectl-cli.md) — emitted by the hidden `hivectl
|
|
markdown-docs` subcommand and kept in lockstep with the code by the
|
|
`hivectl-docs` flake check (CI fails if the committed copy drifts).
|
|
Regenerate with `nix build .#default && ./result/bin/hivectl
|
|
markdown-docs > docs/tools/hivectl-cli.md`.
|
|
|
|
## Forge
|
|
|
|
Manual entry to the same idempotent provisioning flow `hive-c0re` runs
|
|
at boot. Useful for recovery, ad-hoc re-provisioning, or fixing a single
|
|
agent without bouncing the daemon.
|
|
|
|
```bash
|
|
hivectl forge create-user iris # provision (or refresh) forge account for agent `iris`
|
|
hivectl forge create-user mara # create forge account for a human user; prints token to stdout
|
|
hivectl forge create-user mara --password hunter2 # set a web-login password
|
|
hivectl forge create-user mara --password-stdin # read password from stdin (safer for scripting)
|
|
|
|
hivectl forge reconcile-config iris # show local-applied <-> forge config divergence, then prompt
|
|
hivectl forge reconcile-config iris --from forge # reset local applied checkout to forge main (effective next deploy)
|
|
hivectl forge reconcile-config iris --verbose # include the full diff, not just --stat
|
|
```
|
|
|
|
- For **agents** (name has a state dir under `/var/lib/hyperhive/agents/`):
|
|
token is persisted to `<state>/forge-token`. Re-running refreshes the
|
|
token (idempotent — scope always matches current `TOKEN_SCOPES`).
|
|
- For **non-agents** (humans): creates the account and prints the token to
|
|
stdout; no state dir is created. Re-running after account already exists
|
|
re-mints the token and prints it again — safe for password resets.
|
|
- Without `--password` / `--password-stdin` a random throwaway password
|
|
is used (fine for agents — they auth by token).
|
|
- `reconcile-config <agent>` shows the divergence between the agent's local
|
|
applied config checkout and its forge `agent-configs/<agent>` `main`, then
|
|
reconciles. `--from forge` resets the local checkout to forge `main` (takes
|
|
effect on the next deploy — it does not auto-rebuild). `--from local` is not
|
|
supported yet (forge `main` is core-only branch-protected; resolve via a
|
|
config PR). With no `--from` it prompts for the direction after the diff.
|
|
|
|
## Matrix
|
|
|
|
Manual entry to the same idempotent matrix provisioning flow
|
|
`hive-c0re` runs at boot. Requires the `hive-matrix` container to be
|
|
running (`services.hyperhive.matrix.enable = true`).
|
|
|
|
```bash
|
|
hivectl matrix create-user iris # provision (or re-provision) matrix account for agent `iris`
|
|
hivectl matrix create-user mara # create matrix account for a human; prints access_token to stdout
|
|
hivectl matrix create-user mara --password hunter2 # set a client-login password
|
|
hivectl matrix sync-admin # provision / refresh the hive internal admin account
|
|
hivectl matrix promote-user mara # promote an existing matrix user to homeserver admin
|
|
hivectl matrix reset-password iris # generate and set a new random password for `iris`; prints it
|
|
hivectl matrix invite mara # invite a user to the hive Space
|
|
hivectl matrix invite @mara:server --room '#hive-chat:server' # ...or to a specific room/alias
|
|
```
|
|
|
|
- `create-user`: for agents, persists the `access_token` to
|
|
`<state>/matrix-token`. Skips registration when the file already
|
|
exists — delete it first to force re-registration.
|
|
- `sync-admin`: ensures the hive's internal admin matrix user exists
|
|
(used by `hive-c0re` for admin-room commands). Token persisted to the
|
|
admin token path. Safe to run again — idempotent.
|
|
- `promote-user`: promotes an already-registered user to homeserver
|
|
admin via the matrix admin API. Requires `sync-admin` to have run
|
|
first (needs a valid admin token).
|
|
- `reset-password`: calls the matrix admin API to set a new random
|
|
password and prints it to stdout. Useful if an agent or human lost
|
|
credentials.
|
|
- `invite`: invites a matrix user (full `@user:server` or a bare
|
|
localpart, qualified with the homeserver's `server_name`) to the hive
|
|
Space by default, or to a `--room` id / `#alias`. Uses the hive admin
|
|
token; the admin account must be a member of the target room with
|
|
invite power (it owns the hive Space, so that case always works).
|
|
Idempotent — already-member / already-invited is a no-op.
|
|
|
|
## GitHub
|
|
|
|
Write an operator-supplied GitHub personal access token (PAT) into an
|
|
agent's token file so its `gh` wrapper + git credential helper can act as
|
|
the bot account. Unlike forge/matrix there is no account creation — the PAT
|
|
is for an existing GitHub account. A CLI alternative to the dashboard
|
|
credentials tab; the [GitHub integration](../github.md) is on by default
|
|
(`hyperhive.github.enable`), so no per-agent config is needed.
|
|
|
|
```bash
|
|
hivectl github set-token damocles --token-stdin # paste the PAT on stdin (preferred)
|
|
hivectl github set-token damocles --token <pat> # inline (visible in shell history)
|
|
```
|
|
|
|
- `set-token`: writes `<state>/github-token` (`0600`, agent-owned) via
|
|
hive-priv — the same credential-injection path as forge/matrix tokens.
|
|
The `gh` wrapper / git credential helper read it live, so a freshly-set
|
|
or rotated PAT takes effect with no rebuild or restart. Refuses an empty
|
|
token. See [github.md](../github.md) for the full flow + security notes.
|
|
|
|
## Gateway
|
|
|
|
Manage users in the gateway's HTTP Basic auth htpasswd file
|
|
(`services.hyperhive.gateway.auth`). `hivectl` sends the request over the
|
|
host admin socket; the `hive-c0re` daemon owns the htpasswd file at its
|
|
canonical path (`/var/lib/hyperhive/gateway/gateway.htpasswd`) and
|
|
performs the write.
|
|
|
|
```bash
|
|
hivectl gateway create-user alice --password-stdin # add (or update) user; read password from stdin
|
|
hivectl gateway create-user bob --password hunter2 # add user inline (visible in shell history)
|
|
hivectl gateway delete-user bob # remove user
|
|
hivectl gateway list-users # list all usernames, one per line
|
|
```
|
|
|
|
Passwords are hashed with BCrypt (cost 12) by the daemon. The file is
|
|
created if it does not exist. Re-running `create-user` with the same
|
|
username updates the password hash in place.
|
|
|
|
## Agents
|
|
|
|
Container lifecycle shortcuts that go through the host admin socket.
|
|
Requires the `hive-c0re` daemon to be running.
|
|
|
|
```bash
|
|
hivectl agents list # roster: every agent's status + technical state
|
|
hivectl agents list --json # same data as raw JSON rows (for scripting)
|
|
hivectl agents restart iris # stop + start the `iris` container (no rebuild)
|
|
hivectl agents restart-all # stop + start every managed agent container in sequence
|
|
hivectl agents pause iris # park iris's turn loop, leave the container running
|
|
hivectl agents resume iris # let it drive turns again, draining what queued up
|
|
```
|
|
|
|
`list` prints a padded table with one row per managed agent —
|
|
`NAME STATUS REV PARENT REMIND`. STATUS collapses the health flags
|
|
(`running` / `stopped`, plus ` paused` / ` needs-login` / ` needs-update`
|
|
when set — `paused` is orthogonal to running, see below);
|
|
REV is the first 12 chars of the agent's locked config sha; PARENT is its
|
|
place in the topology tree (`-` for a root agent); REMIND is the count of
|
|
pending reminders. It reuses the same per-agent aggregation the dashboard
|
|
renders, so the CLI roster and the web UI never drift. `--json` emits the
|
|
raw rows instead of the table.
|
|
|
|
`restart` is the manual equivalent of the MCP `restart` tool — useful
|
|
when you need to kick a container from the host without going through
|
|
the agent hierarchy. Failures on `restart-all` are collected and
|
|
reported at the end rather than aborting mid-run.
|
|
|
|
`pause` / `resume` are the "stop burning tokens without losing the
|
|
container" pair. Pausing writes a marker file into the agent's harness
|
|
dir (`<state>/<name>/harness/paused`) which the harness re-stats every
|
|
5 s at the top of its serve loop; while it's there the agent drives no
|
|
turns, but the container, its mounts, its warm caches, its web UI and
|
|
its MCP daemons all stay up. Inbox messages queue **unacked**, so a
|
|
resume drains the backlog rather than dropping it. Points worth knowing:
|
|
|
|
- **Sticky.** The marker lives on the persistent harness mount, so a
|
|
paused agent stays paused across a container restart — and pausing a
|
|
*stopped* agent makes it come up parked.
|
|
- **Not a DAG.** Unlike `restart`/`stop`, there's no container operation
|
|
to sequence, so it applies immediately with nothing to wait on.
|
|
- **Stopping a paused agent is still fast.** The graceful-stop
|
|
handshake is skipped for a paused agent (it would never answer), which
|
|
is safe precisely because the pause check sits at the top of the loop:
|
|
a paused agent has no turn in flight to checkpoint.
|
|
- Visible as ` paused` in `agents list`'s STATUS column, as a `paused`
|
|
field on the JSON rows, and as a badge on the dashboard card.
|
|
|
|
### Per-agent resource limits
|
|
|
|
```bash
|
|
hivectl agents set-limits sock --cpu-quota 400% --memory-max 8G
|
|
hivectl agents set-limits sock --memory-max 8G # CPU falls back to the hive default
|
|
hivectl agents set-limits sock --reset # drop all overrides
|
|
```
|
|
|
|
Overrides the hive-wide `services.hyperhive.agentCpuQuota` /
|
|
`agentMemoryMax` for one agent, persisted to
|
|
`meta/resource-limits.json` (see
|
|
[`persistence.md`](../persistence.md)). Values are systemd's
|
|
`CPUQuota=` / `MemoryMax=` syntax: a percentage (`400%` = four full
|
|
cores) for CPU; a size (`8G`), a percentage of physical RAM, or
|
|
`infinity` for memory. Both are validated before they're persisted —
|
|
they go into a systemd drop-in verbatim, and a typo there makes the
|
|
unit fail to start.
|
|
|
|
**Declarative, not incremental**: each invocation replaces the agent's
|
|
whole entry. `set-limits sock --memory-max 8G` leaves `sock` with *only*
|
|
a memory override, reverting any previously-set CPU quota to the hive
|
|
default. To avoid a forgotten flag silently wiping an override, a bare
|
|
`set-limits <name>` with no flags is rejected — clearing requires the
|
|
explicit `--reset`.
|
|
|
|
The command rewrites the container's drop-in and reloads systemd, so
|
|
new containers and restarts pick the values up immediately.
|
|
|
|
## Choom
|
|
|
|
Drop into an interactive Claude session inside an agent container.
|
|
Replaces the current process with `machinectl shell <name>@h-<name>`
|
|
running claude from the agent's state dir. Requires root (same as all
|
|
`machinectl shell` operations).
|
|
|
|
```bash
|
|
hivectl choom iris # fresh blank Claude session in iris's container
|
|
hivectl choom iris --resume <session-id> # rejoin a prior session by id
|
|
```
|
|
|
|
Bare `choom` starts a fresh blank session. `--resume <value>` passes
|
|
through as `claude --resume <value>` to rejoin a prior session by its
|
|
session id — the flag name deliberately matches the claude flag it maps
|
|
to. (choom never uses claude's `--continue`: that's a bare flag that
|
|
takes no argument and resumes the cwd's *latest* session, i.e. the
|
|
harness's; a value after it would be consumed as the first prompt,
|
|
silently poking the live harness session.) A value is required when the
|
|
flag is given. Either way choom never collides with the harness's live
|
|
session in the same project dir: the harness pins its own id via
|
|
`--resume`, so a blank choom session is invisible to it. The container
|
|
must be running.
|
|
|
|
choom reproduces the harness's own claude invocation so the operator
|
|
lands in a faithful copy of the agent's environment:
|
|
|
|
- **as the agent user**, not root: `machinectl shell` defaults to root,
|
|
which would make claude read `/root/.claude` (empty) instead of the
|
|
agent's `/home/<name>/.claude` where its OAuth credentials live. choom
|
|
prefixes the machine with `<name>@` (the meta-flake sets the agent's
|
|
unix user name to its label).
|
|
- **from `/agents/<name>/state`**: the session and `CLAUDE.md` (the
|
|
persona) resolve against the right project dir.
|
|
- **with the harness flags**: `--settings`, `--mcp-config`, and
|
|
`--system-prompt-file` from `/run/hive-config/` — the same files the
|
|
harness writes each turn — so the operator gets the agent's settings,
|
|
the hyperhive/matrix MCP tools, and the role prompt. Each flag is
|
|
included only when its file exists.
|
|
- **without the onboarding walkthrough**: a boot-time oneshot
|
|
(`hive-claude-onboarding`) seeds `hasCompletedOnboarding` and the
|
|
project trust flags into `~/.claude.json` once before the harness
|
|
starts, so the first interactive choom lands straight in a session
|
|
instead of the onboarding/trust dialog the headless harness never
|
|
completes. It's the single place hyperhive touches that file.
|
|
|
|
## Open
|
|
|
|
Print (and best-effort open in a browser) one of the hive's web surfaces.
|
|
Requires the `hive-c0re` daemon to be running.
|
|
|
|
```bash
|
|
hivectl open # operator dashboard (same as `open home`)
|
|
hivectl open home # operator dashboard (https://<domain>/)
|
|
hivectl open forge # the forge (Forgejo) web UI
|
|
hivectl open matrix # the matrix GUI (fluffychat)
|
|
```
|
|
|
|
The URL is resolved from the running daemon (`HostRequest::Urls`), which
|
|
reads the per-surface public URLs from c0re's service env — so custom
|
|
forge / matrix domains resolve correctly instead of assuming
|
|
`forge.<domain>`. The URL is **always printed** (the reliable core, since
|
|
the host is usually headless / driven over SSH), then `xdg-open` is tried
|
|
as a convenience — a missing or failing opener is reported as a note, not
|
|
an error.
|
|
|
|
A surface has no URL when it isn't browser-reachable: `home` needs
|
|
`services.hyperhive.domain`; `forge` needs
|
|
`services.hyperhive.forge.behindGateway = true`; `matrix` needs
|
|
`services.hyperhive.matrix.gui.enable = true`. In those cases the command
|
|
exits with a hint naming the option to set.
|