diff --git a/docs/tools/hivectl-cli.md b/docs/tools/hivectl-cli.md index 49b83751..4b466440 100644 --- a/docs/tools/hivectl-cli.md +++ b/docs/tools/hivectl-cli.md @@ -24,7 +24,6 @@ This document contains the help content for the `hivectl` command-line program. * [`hivectl wg init`↴](#hivectl-wg-init) * [`hivectl wg peer`↴](#hivectl-wg-peer) * [`hivectl wg status`↴](#hivectl-wg-status) -* [`hivectl peer-config`↴](#hivectl-peer-config) * [`hivectl choom`↴](#hivectl-choom) * [`hivectl stop`↴](#hivectl-stop) * [`hivectl start`↴](#hivectl-start) @@ -50,7 +49,6 @@ Sibling to the `hive-c0re` daemon binary. Covers host-side admin operations that * `gateway` — Gateway htpasswd user management. Add, remove, or list users in an htpasswd file used by the gateway's HTTP Basic auth (`services.hyperhive.gateway.auth`). Credentials are stored as `BCrypt` hashes — no extra service or PAM required * `agents` — Agent container management. Requires the hive-c0re daemon to be running (connects to the host admin socket) * `wg` — WireGuard inter-hive mesh setup helpers (`services.hyperhive.swarm`) -* `peer-config` — Generate the federation peer-config block for THIS hive — the nix a peer operator pastes into their `services.hyperhive.swarm.peers` to trust + reach this hive. Emits `caCert` (+ a `cp` line for the cert) when this hive serves a self-signed CA, the WireGuard public key when the mesh key exists, and the `wireguard{Address,Endpoint}` you pass. The hive's own domain is filled in automatically from the running daemon (`services.hyperhive.domain`). Reads local state (the TLS CA cert, the wg key); never mutates. `wg init` calls this at the end, so a fresh mesh setup prints the hand-over block too * `choom` — Open an interactive Claude session inside an agent container * `stop` — Stop containers hive-wide in one operator action. Bare `hivectl stop` stops **everything** — all sub-agents plus the ci, forge, gateway, and matrix infra containers. Narrow it with scope flags: `--agents` (all sub-agents), `--ci` / `--forge` / `--gateway` / `--matrix` (named infra), and `--agent ` (repeatable) for specific sub-agents. Flags are additive (e.g. `--agents --matrix`). Requires the hive-c0re daemon (connects to the host admin socket). hive-c0re itself is never stopped — it services the request * `start` — Start containers hive-wide — the inverse of `hivectl stop`. Bare `hivectl start` starts everything back up; the same scope flags as `stop` narrow it (`--agents`, `--ci`, `--forge`, `--gateway`, `--matrix`, `--agent `). Requires the hive-c0re daemon @@ -346,19 +344,6 @@ Show the live mesh interface state (`wg show wg-hive`). Requires the mesh to be -## `hivectl peer-config` - -Generate the federation peer-config block for THIS hive — the nix a peer operator pastes into their `services.hyperhive.swarm.peers` to trust + reach this hive. Emits `caCert` (+ a `cp` line for the cert) when this hive serves a self-signed CA, the WireGuard public key when the mesh key exists, and the `wireguard{Address,Endpoint}` you pass. The hive's own domain is filled in automatically from the running daemon (`services.hyperhive.domain`). Reads local state (the TLS CA cert, the wg key); never mutates. `wg init` calls this at the end, so a fresh mesh setup prints the hand-over block too - -**Usage:** `hivectl peer-config [OPTIONS]` - -###### **Options:** - -* `--wg-address ` — This hive's WireGuard mesh address (e.g. `10.42.0.1/32`), emitted as `wireguardAddress`. Omit when not running the mesh -* `--wg-endpoint ` — This hive's public WireGuard endpoint (`host:port`), emitted as `wireguardEndpoint`. Omit when peers dial in / no mesh - - - ## `hivectl choom` Open an interactive Claude session inside an agent container. diff --git a/hive-c0re/src/bin/hivectl.rs b/hive-c0re/src/bin/hivectl.rs index a7c1c25d..17d328c7 100644 --- a/hive-c0re/src/bin/hivectl.rs +++ b/hive-c0re/src/bin/hivectl.rs @@ -89,25 +89,6 @@ enum Cmd { #[command(subcommand)] cmd: WgCmd, }, - /// Generate the federation peer-config block for THIS hive — the nix - /// a peer operator pastes into their `services.hyperhive.swarm.peers` - /// to trust + reach this hive. Emits `caCert` (+ a `cp` line for the - /// cert) when this hive serves a self-signed CA, the WireGuard public - /// key when the mesh key exists, and the `wireguard{Address,Endpoint}` - /// you pass. The hive's own domain is filled in automatically from the - /// running daemon (`services.hyperhive.domain`). Reads local state (the - /// TLS CA cert, the wg key); never mutates. `wg init` calls this at the - /// end, so a fresh mesh setup prints the hand-over block too. - 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, - /// This hive's public WireGuard endpoint (`host:port`), emitted as - /// `wireguardEndpoint`. Omit when peers dial in / no mesh. - #[arg(long)] - wg_endpoint: Option, - }, /// Open an interactive Claude session inside an agent container. /// /// Replaces the current process with `machinectl shell @@ -581,7 +562,7 @@ async fn main() -> Result<()> { AgentsCmd::RestartAll => agents_restart_all(&socket).await, }, Cmd::Wg { cmd } => match cmd { - WgCmd::Init { address } => wg_init(&socket, address.as_deref()).await, + WgCmd::Init { address } => wg_init(address.as_deref()), WgCmd::Peer { domain, pubkey, @@ -593,14 +574,6 @@ async fn main() -> Result<()> { } WgCmd::Status => wg_status(), }, - Cmd::PeerConfig { - wg_address, - wg_endpoint, - } => { - let domain = require_hive_domain(&socket).await?; - peer_config(&domain, wg_address.as_deref(), wg_endpoint.as_deref()); - Ok(()) - } Cmd::Stop { scope, graceful } => stop(&socket, scope.to_scope(), graceful).await, Cmd::Start { scope } => start(&socket, scope.to_scope()).await, Cmd::Restart { scope, graceful } => restart(&socket, scope.to_scope(), graceful).await, @@ -639,41 +612,9 @@ const WG_KEY_PATH: &str = "/etc/wireguard/hive.key"; /// The mesh interface name hive-c0re's nix module brings up. const WG_INTERFACE: &str = "wg-hive"; -/// Host path of this hive's self-signed CA cert (matches the -/// `services.hyperhive.tls.stateDir` default in hive-tls.nix). Its -/// existence means the gateway serves a self-signed, hive-CA-signed leaf, -/// so a federating peer needs this CA via `swarm.peers..caCert`. Absent -/// = ACME / operator cert (trusted by the default CA bundle, no `caCert`). -const HIVE_TLS_CA_PATH: &str = "/var/lib/hive-tls/ca.pem"; - -/// Best-effort query for this hive's domain from the running daemon -/// (`HostRequest::HiveDomain`, which reads `HYPERHIVE_HIVE_DOMAIN` from -/// c0re's service env). `None` when the daemon is unreachable or the -/// domain is unset — callers decide whether that's fatal. -async fn query_hive_domain(socket: &Path) -> Option { - hive_c0re::client::request(socket, hive_sh4re::HostRequest::HiveDomain) - .await - .ok() - .and_then(|r| r.domain) -} - -/// Require this hive's domain from the daemon for snippet generation. -/// Errors with a clear hint when it can't be resolved, so `peer-config` -/// never silently emits a wrong key. -async fn require_hive_domain(socket: &Path) -> Result { - query_hive_domain(socket).await.context( - "could not determine this hive's domain from the daemon — is hive-c0re running \ - and `services.hyperhive.domain` set?", - ) -} - /// `wg init` — generate (if absent) the hive's WireGuard key, print its -/// public key + the nix snippet to enable the mesh, then (best-effort) -/// the `peer-config` block peers paste to federate with this hive, so a -/// fresh setup is one command. The domain comes from the daemon; if it -/// can't be resolved, the peer block is skipped (init still succeeds — -/// its core job is enabling the mesh locally). -async fn wg_init(socket: &Path, address: Option<&str>) -> Result<()> { +/// public key + the nix snippet to enable the mesh. +fn wg_init(address: Option<&str>) -> Result<()> { use std::os::unix::fs::PermissionsExt as _; let key_path = Path::new(WG_KEY_PATH); if key_path.exists() { @@ -716,14 +657,6 @@ async fn wg_init(socket: &Path, address: Option<&str>) -> Result<()> { println!(" address = \"{addr}\";"); println!(" # listenPort = 51820; # default"); println!(" }};"); - - // Also print the block a peer pastes to federate with us (CA + this - // mesh key) — one-stop setup. Domain comes from the daemon; - // best-effort, so init still succeeds when it can't be resolved. - if let Some(d) = query_hive_domain(socket).await { - println!(); - peer_config(&d, address, None); - } Ok(()) } @@ -769,51 +702,6 @@ fn wg_peer(domain: &str, pubkey: &str, address: &str, endpoint: Option<&str>) { println!(" }};"); } -/// `peer-config` — print the `swarm.peers.""` block a peer -/// operator pastes to federate with THIS hive, plus a `cp` line for the -/// CA when this hive is self-signed. Reads local state only (the TLS CA -/// cert presence + the wg key); prints, never mutates. -fn peer_config(domain: &str, wg_address: Option<&str>, wg_endpoint: Option<&str>) { - let self_signed = Path::new(HIVE_TLS_CA_PATH).exists(); - // CA filename derived from the first DNS label so multiple peers' - // certs don't collide in the operator's config dir. - let ca_file = format!("{}-ca.pem", domain.split('.').next().unwrap_or("peer")); - // WireGuard public key, when this hive has a mesh key. Best-effort: - // a missing key or absent `wg` binary just omits the mesh lines. - let wg_pub = std::fs::read(WG_KEY_PATH) - .ok() - .and_then(|k| wg_pubkey(&k).ok()); - - if self_signed { - println!("# 1. copy this hive's CA cert next to the peer's config:"); - println!("cp {HIVE_TLS_CA_PATH} ./{ca_file}"); - println!(); - println!("# 2. paste into the peer hive's NixOS config:"); - } else { - println!("# paste into the peer hive's NixOS config:"); - } - println!("services.hyperhive.swarm.peers.\"{domain}\" = {{"); - if self_signed { - println!(" caCert = ./{ca_file};"); - } - if let Some(pk) = &wg_pub { - println!(" wireguardPublicKey = \"{pk}\";"); - } - if let Some(addr) = wg_address { - println!(" wireguardAddress = \"{addr}\";"); - } - if let Some(ep) = wg_endpoint { - println!(" wireguardEndpoint = \"{ep}\";"); - } - println!("}};"); - if !self_signed { - println!( - "# (this hive's cert chains to a public CA — no `caCert` needed; \ - it's trusted by the default bundle.)" - ); - } -} - /// `wg status` — show the live mesh interface (`wg show wg-hive`), /// inheriting stdout so the operator sees it directly. fn wg_status() -> Result<()> { diff --git a/hive-c0re/src/server.rs b/hive-c0re/src/server.rs index 2248cb90..8fc56bc5 100644 --- a/hive-c0re/src/server.rs +++ b/hive-c0re/src/server.rs @@ -114,16 +114,6 @@ async fn dispatch(req: &HostRequest, coord: Arc) -> HostResponse { } HostRequest::Rebuild { name } => handle_rebuild(&coord, name).await?, HostRequest::List => HostResponse::list(lifecycle::list().await?), - // The hive domain is injected into c0re's service env by - // hive-c0re.nix (`HYPERHIVE_HIVE_DOMAIN`); surface it so the - // operator CLI can fill in this hive's own identity. - HostRequest::HiveDomain => HostResponse::hive_domain( - // Treat an empty env value as unset — otherwise the CLI - // would emit `swarm.peers."" = …`, invalid nix. - std::env::var("HYPERHIVE_HIVE_DOMAIN") - .ok() - .filter(|d| !d.is_empty()), - ), HostRequest::Pending => HostResponse::pending(coord.approvals.pending()?), HostRequest::Approve { id } => { actions::approve(coord.clone(), *id).await?; @@ -220,7 +210,6 @@ async fn handle_restart_all() -> Result { error: Some(errors.join("; ")), agents: Some(ok_agents), approvals: None, - domain: None, }) } } @@ -379,7 +368,6 @@ fn finish_lifecycle(ok_items: Vec, errors: &[String]) -> HostResponse { error: Some(errors.join("; ")), agents: Some(ok_items), approvals: None, - domain: None, } } } diff --git a/hive-sh4re/src/lib.rs b/hive-sh4re/src/lib.rs index ed121f15..b49f1963 100644 --- a/hive-sh4re/src/lib.rs +++ b/hive-sh4re/src/lib.rs @@ -44,11 +44,6 @@ pub enum HostRequest { Rebuild { name: String }, /// List managed containers. List, - /// Report this hive's canonical DNS domain - /// (`services.hyperhive.domain`), or `None` when unset. Lets the - /// operator CLI fill in the hive's own identity (e.g. the federation - /// peer-config block) without the operator retyping it. - HiveDomain, /// List pending approval requests. Pending, /// Approve a pending request by id; the action runs immediately. @@ -136,10 +131,6 @@ pub struct HostResponse { pub agents: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub approvals: Option>, - /// This hive's canonical DNS domain — `HiveDomain` result. `None` - /// when the domain is unset (no `services.hyperhive.domain`). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub domain: Option, } /// One row in the approval queue. `commit_ref` is overloaded per @@ -227,7 +218,6 @@ impl HostResponse { error: None, agents: None, approvals: None, - domain: None, } } @@ -238,7 +228,6 @@ impl HostResponse { error: Some(message.into()), agents: None, approvals: None, - domain: None, } } @@ -249,7 +238,6 @@ impl HostResponse { error: None, agents: Some(agents), approvals: None, - domain: None, } } @@ -260,19 +248,6 @@ impl HostResponse { error: None, agents: None, approvals: Some(approvals), - domain: None, - } - } - - /// `HiveDomain` result — this hive's canonical domain (or `None`). - #[must_use] - pub fn hive_domain(domain: Option) -> Self { - Self { - ok: true, - error: None, - agents: None, - approvals: None, - domain, } } }