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
666 lines
24 KiB
Rust
666 lines
24 KiB
Rust
//! hivectl clap command tree: the `Cli` root, the top-level `Cmd`
|
|
//! verb enum, and every subcommand enum + shared args struct.
|
|
|
|
use clap::{Args, Parser, Subcommand};
|
|
use std::path::PathBuf;
|
|
|
|
#[derive(Parser)]
|
|
#[command(
|
|
name = "hivectl",
|
|
about = "hyperhive host CLI — operator-facing administration",
|
|
long_about = "\
|
|
Sibling to the `hive-c0re` daemon binary. Covers host-side admin \
|
|
operations that don't go through the broker — manual user \
|
|
provisioning on the bundled forge + matrix containers, plus future \
|
|
recovery / debugging verbs.\
|
|
"
|
|
)]
|
|
pub struct Cli {
|
|
/// Path to the hive-c0re host admin socket, used by the daemon-assisted
|
|
/// verbs (`agents`, `stop`, `start`). Global: accepted before or after
|
|
/// the subcommand. Verbs that don't talk to the daemon ignore it.
|
|
#[arg(long, global = true, default_value = DEFAULT_HOST_SOCKET)]
|
|
pub(crate) socket: PathBuf,
|
|
#[command(subcommand)]
|
|
pub(crate) cmd: Cmd,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
pub enum Cmd {
|
|
/// Forgejo user provisioning.
|
|
///
|
|
/// Manual entry point to the same idempotent provisioning c0re runs at
|
|
/// boot — for recovery, ad-hoc reprovisioning, or fixing one agent
|
|
/// without bouncing the daemon.
|
|
Forge {
|
|
#[command(subcommand)]
|
|
cmd: ForgeCmd,
|
|
},
|
|
/// matrix-tuwunel user provisioning.
|
|
///
|
|
/// Manual entry point to the same idempotent provisioning c0re runs at
|
|
/// boot — for re-registering an agent the boot sweep skipped, or after
|
|
/// wiping a token file.
|
|
Matrix {
|
|
#[command(subcommand)]
|
|
cmd: MatrixCmd,
|
|
},
|
|
/// GitHub account provisioning.
|
|
///
|
|
/// Store an operator-supplied personal access token (PAT) for an agent
|
|
/// so its `gh` and git can authenticate. No account is created — the
|
|
/// PAT is for an existing GitHub account.
|
|
Github {
|
|
#[command(subcommand)]
|
|
cmd: GithubCmd,
|
|
},
|
|
/// Gateway htpasswd user management.
|
|
///
|
|
/// Add, remove, or list users for the gateway's HTTP Basic auth.
|
|
Gateway {
|
|
#[command(subcommand)]
|
|
cmd: GatewayCmd,
|
|
},
|
|
/// Agent container management.
|
|
///
|
|
/// Lifecycle actions on managed agent containers. Needs the hive-c0re
|
|
/// daemon running.
|
|
Agents {
|
|
#[command(subcommand)]
|
|
cmd: AgentsCmd,
|
|
},
|
|
/// Operator approval queue: list, approve, or deny pending requests.
|
|
///
|
|
/// Needs the hive-c0re daemon running.
|
|
Approvals {
|
|
#[command(subcommand)]
|
|
cmd: ApprovalsCmd,
|
|
},
|
|
/// WireGuard inter-hive mesh setup helpers.
|
|
///
|
|
/// Generate this hive's mesh key and print the nix to enable the mesh,
|
|
/// add a peer, or inspect live interface state.
|
|
Wg {
|
|
#[command(subcommand)]
|
|
cmd: WgCmd,
|
|
},
|
|
/// Generate the federation peer-config block for THIS hive.
|
|
///
|
|
/// Prints the nix a peer operator pastes into their swarm config to
|
|
/// trust and reach this hive.
|
|
PeerConfig {
|
|
/// This hive's WireGuard mesh address (e.g. `10.42.0.1/32`),
|
|
/// emitted as `wireguardAddress`. Omit when not running the mesh.
|
|
#[arg(long)]
|
|
wg_address: Option<String>,
|
|
/// This hive's public WireGuard endpoint (`host:port`), emitted as
|
|
/// `wireguardEndpoint`. Omit when peers dial in / no mesh.
|
|
#[arg(long)]
|
|
wg_endpoint: Option<String>,
|
|
},
|
|
/// Open an interactive Claude session inside an agent container.
|
|
///
|
|
/// A fresh session by default, or resume a prior one. Requires root
|
|
/// and a running container.
|
|
Choom {
|
|
/// Agent name (e.g. `damocles`, `iris`).
|
|
name: String,
|
|
/// Resume a prior claude session by its session id, passed
|
|
/// through as `claude --resume <value>` (claude's `--continue`
|
|
/// takes no value — it resumes the cwd's latest session, which
|
|
/// is the harness's, so choom never uses it; this flag matches
|
|
/// the claude flag it maps to). Omit for a fresh blank session.
|
|
/// A value is required when the flag is given.
|
|
#[arg(long = "resume", value_name = "SESSION")]
|
|
resume_session: Option<String>,
|
|
},
|
|
/// Stop containers hive-wide in one operator action.
|
|
///
|
|
/// Bare `hivectl stop` stops everything; scope flags narrow it to
|
|
/// specific sub-agents or infra containers.
|
|
Stop {
|
|
#[command(flatten)]
|
|
scope: ScopeArgs,
|
|
/// Gracefully quiesce each agent before stopping, instead of a
|
|
/// hard stop. Each agent gets a graceful-stop DAG on the job
|
|
/// queue: the harness is signalled, runs one stop-checkpoint
|
|
/// turn to flush durable `/state`, drains, then the container
|
|
/// is stopped (bounded by a 3-min timeout that falls back to a
|
|
/// hard stop). All drains overlap. Applies to agents only.
|
|
#[arg(long)]
|
|
graceful: bool,
|
|
/// Return immediately after the stop DAGs are queued instead of
|
|
/// waiting for them with live per-node progress.
|
|
#[arg(long)]
|
|
no_wait: bool,
|
|
},
|
|
/// Start containers hive-wide — the inverse of `hivectl stop`.
|
|
///
|
|
/// Bare `hivectl start` restores the agents stopped by the last
|
|
/// broad-scope `stop` (or starts everything if none); scope flags
|
|
/// narrow it.
|
|
Start {
|
|
#[command(flatten)]
|
|
scope: ScopeArgs,
|
|
/// Return immediately after the start DAGs are queued instead
|
|
/// of waiting for them with live per-node progress.
|
|
#[arg(long)]
|
|
no_wait: bool,
|
|
},
|
|
/// Restart containers hive-wide — `stop` then `start` over one scope.
|
|
///
|
|
/// Bare `hivectl restart` restarts everything; scope flags narrow it.
|
|
Restart {
|
|
#[command(flatten)]
|
|
scope: ScopeArgs,
|
|
/// Gracefully quiesce each agent on the stop half (see
|
|
/// `stop --graceful`). Applies to agents only.
|
|
#[arg(long)]
|
|
graceful: bool,
|
|
},
|
|
/// Per-agent disk accounting + optional quotas via btrfs qgroups.
|
|
///
|
|
/// Opt-in: enable qgroup accounting, then report per-agent usage or
|
|
/// cap an agent. No-op on non-btrfs hosts.
|
|
Quota {
|
|
#[command(subcommand)]
|
|
cmd: QuotaCmd,
|
|
},
|
|
/// btrfs subvolume management for agent state dirs.
|
|
///
|
|
/// Upgrade an existing plain-dir agent's state into a btrfs subvolume
|
|
/// so it gains snapshots and per-subvol usage/quota.
|
|
Subvol {
|
|
#[command(subcommand)]
|
|
cmd: SubvolCmd,
|
|
},
|
|
/// Print (and best-effort open in a browser) a hive web surface URL.
|
|
///
|
|
/// Resolves the URL from the running daemon so custom forge / matrix
|
|
/// domains work. Bare `hivectl open` opens the operator dashboard.
|
|
Open {
|
|
/// Which surface to open. Defaults to the operator dashboard.
|
|
#[arg(value_enum, default_value_t = OpenTarget::Home)]
|
|
target: OpenTarget,
|
|
},
|
|
/// Emit the full CLI reference as `CommonMark` to stdout.
|
|
///
|
|
/// Hidden tooling command used by the docs build to keep the published
|
|
/// `hivectl` reference in lockstep with the code.
|
|
#[command(hide = true)]
|
|
MarkdownDocs,
|
|
/// Generate a shell completion script for `hivectl` and print it to
|
|
/// stdout.
|
|
///
|
|
/// Supports bash, zsh, fish, elvish, and powershell. The NixOS module
|
|
/// already installs the zsh script system-wide; this is for ad-hoc or
|
|
/// other-shell use.
|
|
Completions {
|
|
/// Shell to emit completions for.
|
|
shell: clap_complete::Shell,
|
|
},
|
|
}
|
|
|
|
/// Which hive web surface `hivectl open` targets.
|
|
#[derive(Copy, Clone, Debug, clap::ValueEnum)]
|
|
pub enum OpenTarget {
|
|
/// The operator dashboard (`https://<domain>/`).
|
|
Home,
|
|
/// The forge (Forgejo) web UI.
|
|
Forge,
|
|
/// The matrix GUI (fluffychat).
|
|
Matrix,
|
|
}
|
|
|
|
/// Shared scope flags for `hivectl stop` / `hivectl start`. With no flag
|
|
/// set the verb targets **everything** (all sub-agents + every controllable
|
|
/// infra container). Setting any flag restricts to the selected classes,
|
|
/// additively.
|
|
// One bool per selectable container class, each mapping 1:1 to a clap flag;
|
|
// orthogonal toggles, not a state machine — hence the bools allow (mirrors
|
|
// `hive_host_sock::LifecycleScope`).
|
|
#[allow(clippy::struct_excessive_bools)]
|
|
#[derive(Args)]
|
|
pub struct ScopeArgs {
|
|
/// All sub-agent containers.
|
|
#[arg(long)]
|
|
agents: bool,
|
|
/// A specific sub-agent by name. Repeatable: `--agent a --agent b`.
|
|
#[arg(long = "agent", value_name = "NAME")]
|
|
agent: Vec<String>,
|
|
/// The CI runner container (`hive-ci`).
|
|
#[arg(long)]
|
|
ci: bool,
|
|
/// The forge container (`hive-forge`).
|
|
#[arg(long)]
|
|
forge: bool,
|
|
/// The gateway container (`hive-gateway`).
|
|
#[arg(long)]
|
|
gateway: bool,
|
|
/// The matrix container (`hive-matrix`).
|
|
#[arg(long)]
|
|
matrix: bool,
|
|
}
|
|
|
|
impl ScopeArgs {
|
|
pub(crate) fn to_scope(&self) -> hive_host_sock::LifecycleScope {
|
|
hive_host_sock::LifecycleScope {
|
|
agents: self.agents,
|
|
agent_names: self.agent.clone(),
|
|
ci: self.ci,
|
|
forge: self.forge,
|
|
gateway: self.gateway,
|
|
matrix: self.matrix,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
pub enum ForgeCmd {
|
|
/// Create or refresh the Forgejo account + token for `<name>`.
|
|
///
|
|
/// For an existing agent, persists the token to its state dir; for a
|
|
/// human/other account, prints the token to stdout. Set a password to
|
|
/// enable forge web-UI login (a random throwaway is used otherwise).
|
|
CreateUser {
|
|
/// Forgejo username. For agents: the container/agent name
|
|
/// (`<n>` in `h-<n>`; manager uses the literal `manager`).
|
|
/// For humans: any forgejo username — `mara`, `damocles`, etc.
|
|
name: String,
|
|
/// Set the account password to this string instead of a random
|
|
/// throwaway. Use this for operator accounts that need to log
|
|
/// into the forge web UI. Mutually exclusive with
|
|
/// `--password-stdin`. WARNING: the password is visible in
|
|
/// shell history + process listings; prefer `--password-stdin`
|
|
/// for anything sensitive.
|
|
#[arg(long)]
|
|
password: Option<String>,
|
|
/// Read the password from stdin (single line, trailing newline
|
|
/// stripped) instead of an inline flag. Mutually exclusive with
|
|
/// `--password`.
|
|
#[arg(long, conflicts_with = "password")]
|
|
password_stdin: bool,
|
|
},
|
|
/// Show + reconcile the divergence between an agent's local applied
|
|
/// config checkout and its forge `agent-configs/<agent>` main.
|
|
///
|
|
/// Always prints the diff first. `--from forge` resets the local
|
|
/// checkout to forge main (effective on the next deploy); `--from local`
|
|
/// is not supported yet. With no `--from`, prompts for the direction.
|
|
ReconcileConfig {
|
|
/// Agent whose config branches to reconcile.
|
|
agent: String,
|
|
/// Which side to reconcile from. Omit to be prompted after the diff.
|
|
#[arg(long, value_enum)]
|
|
from: Option<ReconcileFrom>,
|
|
/// Include the full diff (not just `--stat`) in the report.
|
|
#[arg(long)]
|
|
verbose: bool,
|
|
},
|
|
}
|
|
|
|
/// Which side to reconcile config branches from (`hivectl forge
|
|
/// reconcile-config --from`). Maps to [`hive_host_sock::ReconcileDirection`].
|
|
#[derive(Clone, Copy, clap::ValueEnum)]
|
|
pub enum ReconcileFrom {
|
|
/// Reset the local applied checkout to forge main.
|
|
Forge,
|
|
/// Advance forge main from local — not supported yet.
|
|
Local,
|
|
}
|
|
|
|
impl From<ReconcileFrom> for hive_host_sock::ReconcileDirection {
|
|
fn from(from: ReconcileFrom) -> Self {
|
|
match from {
|
|
ReconcileFrom::Forge => Self::Forge,
|
|
ReconcileFrom::Local => Self::Local,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
pub enum MatrixCmd {
|
|
/// Create or refresh the matrix account + access token for `<name>`.
|
|
///
|
|
/// For an existing agent, persists the token to its state dir; for a
|
|
/// human/other account, prints the access token to stdout. Set a
|
|
/// password to enable matrix web-client login (a random throwaway is
|
|
/// used otherwise).
|
|
CreateUser {
|
|
/// Matrix localpart. For agents: the container/agent name.
|
|
/// For humans: any matrix localpart — `mara`, `damocles`, etc.
|
|
name: String,
|
|
/// Set the account password to this string instead of a random
|
|
/// throwaway. Use this for operator accounts that need to log
|
|
/// into matrix web clients via `m.login.password`. Mutually
|
|
/// exclusive with `--password-stdin`. WARNING: the
|
|
/// password is visible in shell history + process listings;
|
|
/// prefer `--password-stdin` for anything sensitive.
|
|
#[arg(long)]
|
|
password: Option<String>,
|
|
/// Read the password from stdin (single line, trailing newline
|
|
/// stripped) instead of an inline flag. Mutually exclusive with
|
|
/// `--password`.
|
|
#[arg(long, conflicts_with = "password")]
|
|
password_stdin: bool,
|
|
},
|
|
/// Provision (or re-provision) the hive system admin matrix account.
|
|
///
|
|
/// Runs automatically on startup; run manually to recover a missing
|
|
/// admin token.
|
|
SyncAdmin,
|
|
/// Promote a matrix user to homeserver admin.
|
|
PromoteUser {
|
|
/// Matrix localpart of the user to promote (e.g. `argus`).
|
|
name: String,
|
|
},
|
|
/// Reset a matrix user's password via the admin API.
|
|
///
|
|
/// Persists the new password so a later `create-user` can re-login.
|
|
ResetPassword {
|
|
/// Matrix localpart of the account to reset (e.g. `argus`).
|
|
name: String,
|
|
},
|
|
/// Invite a matrix user to the hive Space, or a specific room with
|
|
/// `--room`. Idempotent.
|
|
Invite {
|
|
/// User to invite: a full id (`@mara:server`) or a bare
|
|
/// localpart (qualified with the homeserver's `server_name`).
|
|
user: String,
|
|
/// Target room id (`!abc:server`) or alias (`#name:server`).
|
|
/// Omit to invite to the hive Space.
|
|
#[arg(long)]
|
|
room: Option<String>,
|
|
},
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
pub enum GithubCmd {
|
|
/// Store a GitHub PAT for `<agent>` so its `gh` and git can
|
|
/// authenticate.
|
|
///
|
|
/// Prefer `--token-stdin` — an inline `--token` is visible in shell
|
|
/// history.
|
|
SetToken {
|
|
/// Logical agent name (the container/agent name).
|
|
agent: String,
|
|
/// The PAT value inline. Mutually exclusive with `--token-stdin`.
|
|
#[arg(long)]
|
|
token: Option<String>,
|
|
/// Read the PAT from stdin (trailing newline stripped). Mutually
|
|
/// exclusive with `--token`.
|
|
#[arg(long, conflicts_with = "token")]
|
|
token_stdin: bool,
|
|
},
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
pub enum GatewayCmd {
|
|
/// Add a user or update an existing user's password in the gateway
|
|
/// htpasswd.
|
|
///
|
|
/// Use `--password-stdin` to keep the password out of shell history.
|
|
CreateUser {
|
|
/// Username to add or update.
|
|
username: String,
|
|
/// Set the password inline. WARNING: visible in shell history and
|
|
/// process listings — prefer `--password-stdin` for sensitive input.
|
|
/// Mutually exclusive with `--password-stdin`.
|
|
#[arg(long, conflicts_with = "password_stdin")]
|
|
password: Option<String>,
|
|
/// Read the password from stdin (single line, trailing newline
|
|
/// stripped). Mutually exclusive with `--password`.
|
|
#[arg(long)]
|
|
password_stdin: bool,
|
|
},
|
|
/// Remove a user from the gateway htpasswd.
|
|
DeleteUser {
|
|
/// Username to remove.
|
|
username: String,
|
|
},
|
|
/// List all gateway htpasswd usernames, one per line.
|
|
ListUsers,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
pub enum WgCmd {
|
|
/// Generate this hive's WireGuard key (if absent) and print its public
|
|
/// key plus the nix to enable the mesh.
|
|
Init {
|
|
/// This hive's mesh address (e.g. `10.42.0.1/32`) to bake into the
|
|
/// printed snippet. Omit to get a placeholder you fill in.
|
|
#[arg(long)]
|
|
address: Option<String>,
|
|
},
|
|
/// Print the nix to add a peer hive to the mesh.
|
|
Peer {
|
|
/// Peer hive's DNS domain (the `swarm.peers` attrset key).
|
|
domain: String,
|
|
/// Peer's WireGuard public key (from its `hivectl wg init`).
|
|
#[arg(long)]
|
|
pubkey: String,
|
|
/// Peer's mesh address (e.g. `10.42.0.2/32`).
|
|
#[arg(long)]
|
|
address: String,
|
|
/// Peer's `host:port` endpoint (omit for a peer that only dials out,
|
|
/// e.g. one behind NAT — it must set an endpoint pointing back here).
|
|
#[arg(long)]
|
|
endpoint: Option<String>,
|
|
},
|
|
/// Show the live mesh interface state.
|
|
Status,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
pub enum QuotaCmd {
|
|
/// Enable btrfs qgroup accounting on the agent-state filesystem.
|
|
///
|
|
/// Run once before `show` / `limit`. No-op on non-btrfs hosts.
|
|
Enable,
|
|
/// Report per-agent disk usage from btrfs qgroups (all agents, or one
|
|
/// by name).
|
|
Show {
|
|
/// Agent to show (omit for all agents with a state subvolume).
|
|
name: Option<String>,
|
|
},
|
|
/// Set or clear an agent's disk-usage quota.
|
|
Limit {
|
|
/// Agent whose state subvolume to limit.
|
|
name: String,
|
|
/// Size cap (`5G`, `500M`, `1073741824`) or `none` to clear.
|
|
size: String,
|
|
},
|
|
}
|
|
|
|
// Default host admin socket path. Shared with `hive-c0re`'s `main.rs`
|
|
// default via `hive_host_sock::HOST_SOCKET` — the daemon binds there
|
|
// and `hivectl agents` connects to it.
|
|
pub(crate) use hive_host_sock::HOST_SOCKET as DEFAULT_HOST_SOCKET;
|
|
|
|
#[derive(Subcommand)]
|
|
pub enum AgentsCmd {
|
|
/// Show all managed agents with their status and technical state.
|
|
List {
|
|
/// Emit the raw JSON rows instead of the padded table (for
|
|
/// scripting). The table is the default human-readable shape.
|
|
#[arg(long)]
|
|
json: bool,
|
|
},
|
|
/// Stop and start a single agent container without rebuilding config.
|
|
Restart {
|
|
/// Agent name (e.g. `damocles`, `ruth`).
|
|
name: String,
|
|
/// Return immediately after the restart DAG is queued.
|
|
#[arg(long)]
|
|
no_wait: bool,
|
|
},
|
|
/// Restart all managed agent containers.
|
|
RestartAll {
|
|
/// Return immediately after the restart DAGs are queued.
|
|
#[arg(long)]
|
|
no_wait: bool,
|
|
},
|
|
/// Park an agent's turn loop, leaving the container running.
|
|
///
|
|
/// The harness stops driving turns but keeps serving its web UI and
|
|
/// MCP daemons, so the container, its mounts and its warm caches stay
|
|
/// up while it burns no tokens. Inbox messages queue unacked and the
|
|
/// backlog drains on `resume`. Sticky: it survives a restart, and
|
|
/// pausing a stopped agent makes it come up paused.
|
|
Pause {
|
|
/// Agent name.
|
|
name: String,
|
|
},
|
|
/// Resume a paused agent — it drains whatever queued up while parked.
|
|
Resume {
|
|
/// Agent name.
|
|
name: String,
|
|
},
|
|
/// Spawn a new agent container directly, bypassing the approval queue.
|
|
///
|
|
/// Operator-on-the-host only; use `request-spawn` for an approval-gated
|
|
/// spawn.
|
|
Spawn {
|
|
/// Agent name (e.g. `iris`).
|
|
name: String,
|
|
},
|
|
/// Queue a spawn request for operator approval.
|
|
RequestSpawn {
|
|
/// Agent name.
|
|
name: String,
|
|
},
|
|
/// Stop a managed container (graceful).
|
|
Kill {
|
|
/// Agent name.
|
|
name: String,
|
|
},
|
|
/// Tear down a sub-agent container, keeping its state by default. No
|
|
/// undo.
|
|
Destroy {
|
|
/// Agent name.
|
|
name: String,
|
|
/// Also wipe the agent's state dirs (config + creds + notes).
|
|
#[arg(long)]
|
|
purge: bool,
|
|
},
|
|
/// Apply pending config to a managed container.
|
|
Rebuild {
|
|
/// Agent name.
|
|
name: String,
|
|
},
|
|
/// Move an agent in the topology tree — under a new parent, or to root.
|
|
SetParent {
|
|
/// Agent to move.
|
|
child: String,
|
|
/// New parent agent name. Mutually exclusive with `--root`.
|
|
#[arg(long, conflicts_with = "root", required_unless_present = "root")]
|
|
parent: Option<String>,
|
|
/// Promote `child` to root (no parent).
|
|
#[arg(long)]
|
|
root: bool,
|
|
},
|
|
/// Declare an agent's CPU/memory limits, overriding the hive-wide defaults.
|
|
///
|
|
/// Replaces the agent's whole override entry rather than merging into
|
|
/// it: any limit you don't pass returns to the hive-wide default. To
|
|
/// change one and keep the other, pass both.
|
|
SetLimits {
|
|
/// Agent name.
|
|
name: String,
|
|
/// systemd `CPUQuota=` value, e.g. `400%` (100% = one full core).
|
|
#[arg(long, conflicts_with = "reset")]
|
|
cpu_quota: Option<String>,
|
|
/// systemd `MemoryMax=` value, e.g. `8G`, `50%`, or `infinity`.
|
|
#[arg(long, conflicts_with = "reset")]
|
|
memory_max: Option<String>,
|
|
/// Drop all overrides — the agent returns to the hive-wide
|
|
/// defaults. Required to clear limits, so that a `set-limits`
|
|
/// with a forgotten value can't silently reset the agent.
|
|
#[arg(
|
|
long,
|
|
conflicts_with_all = ["cpu_quota", "memory_max"],
|
|
required_unless_present_any = ["cpu_quota", "memory_max"],
|
|
)]
|
|
reset: bool,
|
|
},
|
|
}
|
|
|
|
/// Operator approval queue: list, approve, or deny pending requests.
|
|
#[derive(Subcommand)]
|
|
pub enum ApprovalsCmd {
|
|
/// List pending approval requests submitted by agents.
|
|
Pending,
|
|
/// Approve a pending request by id; the action runs immediately.
|
|
Approve {
|
|
/// Approval id (from `hivectl approvals pending`).
|
|
id: i64,
|
|
},
|
|
/// Deny a pending request by id.
|
|
Deny {
|
|
/// Approval id.
|
|
id: i64,
|
|
},
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
pub enum SubvolCmd {
|
|
/// Convert a plain-dir agent state root into a btrfs subvolume in
|
|
/// place, so it gains snapshots and per-subvol usage/quota.
|
|
///
|
|
/// Bounces the agent to migrate its state, so it requires `--yes`.
|
|
Upgrade {
|
|
/// Agent name (e.g. `damocles`, `iris`).
|
|
name: String,
|
|
/// Confirm: this stops the agent, migrates its state dir, and
|
|
/// restarts it. Required — the command refuses without it.
|
|
#[arg(long)]
|
|
yes: bool,
|
|
},
|
|
/// Read-only snapshots of an agent's state subvolume.
|
|
Snapshot {
|
|
#[command(subcommand)]
|
|
cmd: SnapshotCmd,
|
|
},
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
pub enum SnapshotCmd {
|
|
/// Create a read-only snapshot (agent must already be a subvolume).
|
|
Create {
|
|
/// Agent name (e.g. `damocles`, `iris`).
|
|
name: String,
|
|
/// Snapshot label. Mandatory, and must start with `hive-` — the
|
|
/// prefix doubles as an allow-list hive-priv checks so only
|
|
/// hivectl-issued snapshot names can reach the `btrfs subvolume
|
|
/// snapshot` shellout.
|
|
#[arg(long)]
|
|
label: String,
|
|
},
|
|
/// Delete a snapshot created by `subvol snapshot create`.
|
|
Delete {
|
|
/// Agent name the snapshot belongs to.
|
|
name: String,
|
|
/// Snapshot label passed to `subvol snapshot create --label`.
|
|
label: String,
|
|
},
|
|
/// Export a snapshot to a local file via `btrfs send` (the local-file
|
|
/// half of inter-hive migration transport; the cross-hive `ssh ...
|
|
/// btrfs receive` leg isn't wired up yet). Also useful standalone as a
|
|
/// point-in-time backup: a full send with no `--parent` produces a
|
|
/// self-contained archive of the snapshot.
|
|
Send {
|
|
/// Agent name the snapshot belongs to.
|
|
name: String,
|
|
/// Snapshot label passed to `subvol snapshot create --label`.
|
|
label: String,
|
|
/// Optional parent snapshot label for an incremental send
|
|
/// (`btrfs send -p`) — must be an existing, older snapshot of the
|
|
/// same agent. Omit for a full send.
|
|
#[arg(long)]
|
|
parent: Option<String>,
|
|
/// Destination filename (not a path) under the migrate-staging
|
|
/// dir. Refused if it already exists.
|
|
#[arg(long)]
|
|
dest: String,
|
|
},
|
|
}
|