feat(#1897): always auto-fill the hive domain — drop the --domain override
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.
This commit is contained in:
parent
72d9422a7a
commit
8ea19b3b12
2 changed files with 20 additions and 44 deletions
|
|
@ -50,7 +50,7 @@ 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
|
* `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)
|
* `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`)
|
* `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. Reads local state (the TLS CA cert, the wg key) + asks the daemon for this hive's domain; never mutates. `wg init` calls this at the end, so a fresh mesh setup prints the hand-over block too
|
* `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
|
* `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 <name>` (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
|
* `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 <name>` (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 <name>`). Requires the hive-c0re daemon
|
* `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 <name>`). Requires the hive-c0re daemon
|
||||||
|
|
@ -317,7 +317,6 @@ Generate (if absent) this hive's WireGuard private key, print its public key, an
|
||||||
###### **Options:**
|
###### **Options:**
|
||||||
|
|
||||||
* `--address <ADDRESS>` — 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
|
* `--address <ADDRESS>` — 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
|
||||||
* `--domain <DOMAIN>` — This hive's DNS domain. When set, `init` also prints the `peer-config` block peers paste to federate with this hive (CA + this mesh key), so setup is one command. Omit to skip that and just enable the mesh locally
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -349,13 +348,12 @@ Show the live mesh interface state (`wg show wg-hive`). Requires the mesh to be
|
||||||
|
|
||||||
## `hivectl peer-config`
|
## `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. Reads local state (the TLS CA cert, the wg key) + asks the daemon for this hive's domain; never mutates. `wg init` calls this at the end, so a fresh mesh setup prints the hand-over block too
|
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]`
|
**Usage:** `hivectl peer-config [OPTIONS]`
|
||||||
|
|
||||||
###### **Options:**
|
###### **Options:**
|
||||||
|
|
||||||
* `--domain <DOMAIN>` — Override this hive's DNS domain (the `swarm.peers` attrset key the peer declares). Omit to auto-fill from the running daemon (`services.hyperhive.domain`); pass it only when the daemon is down or you're scripting offline
|
|
||||||
* `--wg-address <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-address <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 <WG_ENDPOINT>` — This hive's public WireGuard endpoint (`host:port`), emitted as `wireguardEndpoint`. Omit when peers dial in / no mesh
|
* `--wg-endpoint <WG_ENDPOINT>` — This hive's public WireGuard endpoint (`host:port`), emitted as `wireguardEndpoint`. Omit when peers dial in / no mesh
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,16 +94,11 @@ enum Cmd {
|
||||||
/// to trust + reach this hive. Emits `caCert` (+ a `cp` line for the
|
/// to trust + reach this hive. Emits `caCert` (+ a `cp` line for the
|
||||||
/// cert) when this hive serves a self-signed CA, the WireGuard public
|
/// cert) when this hive serves a self-signed CA, the WireGuard public
|
||||||
/// key when the mesh key exists, and the `wireguard{Address,Endpoint}`
|
/// key when the mesh key exists, and the `wireguard{Address,Endpoint}`
|
||||||
/// you pass. Reads local state (the TLS CA cert, the wg key) + asks the
|
/// you pass. The hive's own domain is filled in automatically from the
|
||||||
/// daemon for this hive's domain; never mutates. `wg init` calls this
|
/// running daemon (`services.hyperhive.domain`). Reads local state (the
|
||||||
/// at the end, so a fresh mesh setup prints the hand-over block too.
|
/// 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 {
|
PeerConfig {
|
||||||
/// Override this hive's DNS domain (the `swarm.peers` attrset key
|
|
||||||
/// the peer declares). Omit to auto-fill from the running daemon
|
|
||||||
/// (`services.hyperhive.domain`); pass it only when the daemon is
|
|
||||||
/// down or you're scripting offline.
|
|
||||||
#[arg(long)]
|
|
||||||
domain: Option<String>,
|
|
||||||
/// This hive's WireGuard mesh address (e.g. `10.42.0.1/32`),
|
/// This hive's WireGuard mesh address (e.g. `10.42.0.1/32`),
|
||||||
/// emitted as `wireguardAddress`. Omit when not running the mesh.
|
/// emitted as `wireguardAddress`. Omit when not running the mesh.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
|
@ -453,12 +448,6 @@ enum WgCmd {
|
||||||
/// printed snippet. Omit to get a placeholder you fill in.
|
/// printed snippet. Omit to get a placeholder you fill in.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
address: Option<String>,
|
address: Option<String>,
|
||||||
/// This hive's DNS domain. When set, `init` also prints the
|
|
||||||
/// `peer-config` block peers paste to federate with this hive
|
|
||||||
/// (CA + this mesh key), so setup is one command. Omit to skip
|
|
||||||
/// that and just enable the mesh locally.
|
|
||||||
#[arg(long)]
|
|
||||||
domain: Option<String>,
|
|
||||||
},
|
},
|
||||||
/// Print the nix snippet to add a peer hive to the mesh. Pure output —
|
/// Print the nix snippet to add a peer hive to the mesh. Pure output —
|
||||||
/// paste it into this hive's config. Get `<pubkey>` from the peer's
|
/// paste it into this hive's config. Get `<pubkey>` from the peer's
|
||||||
|
|
@ -592,9 +581,7 @@ async fn main() -> Result<()> {
|
||||||
AgentsCmd::RestartAll => agents_restart_all(&socket).await,
|
AgentsCmd::RestartAll => agents_restart_all(&socket).await,
|
||||||
},
|
},
|
||||||
Cmd::Wg { cmd } => match cmd {
|
Cmd::Wg { cmd } => match cmd {
|
||||||
WgCmd::Init { address, domain } => {
|
WgCmd::Init { address } => wg_init(&socket, address.as_deref()).await,
|
||||||
wg_init(&socket, address.as_deref(), domain.as_deref()).await
|
|
||||||
}
|
|
||||||
WgCmd::Peer {
|
WgCmd::Peer {
|
||||||
domain,
|
domain,
|
||||||
pubkey,
|
pubkey,
|
||||||
|
|
@ -607,11 +594,10 @@ async fn main() -> Result<()> {
|
||||||
WgCmd::Status => wg_status(),
|
WgCmd::Status => wg_status(),
|
||||||
},
|
},
|
||||||
Cmd::PeerConfig {
|
Cmd::PeerConfig {
|
||||||
domain,
|
|
||||||
wg_address,
|
wg_address,
|
||||||
wg_endpoint,
|
wg_endpoint,
|
||||||
} => {
|
} => {
|
||||||
let domain = resolve_hive_domain(&socket, domain).await?;
|
let domain = require_hive_domain(&socket).await?;
|
||||||
peer_config(&domain, wg_address.as_deref(), wg_endpoint.as_deref());
|
peer_config(&domain, wg_address.as_deref(), wg_endpoint.as_deref());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -671,27 +657,23 @@ async fn query_hive_domain(socket: &Path) -> Option<String> {
|
||||||
.and_then(|r| r.domain)
|
.and_then(|r| r.domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve this hive's domain for snippet generation: the explicit
|
/// Require this hive's domain from the daemon for snippet generation.
|
||||||
/// `--domain` override if given, else the daemon. Errors with a clear
|
/// Errors with a clear hint when it can't be resolved, so `peer-config`
|
||||||
/// hint when neither is available, so `peer-config` never silently emits
|
/// never silently emits a wrong key.
|
||||||
/// a wrong key.
|
async fn require_hive_domain(socket: &Path) -> Result<String> {
|
||||||
async fn resolve_hive_domain(socket: &Path, over: Option<String>) -> Result<String> {
|
|
||||||
if let Some(d) = over {
|
|
||||||
return Ok(d);
|
|
||||||
}
|
|
||||||
query_hive_domain(socket).await.context(
|
query_hive_domain(socket).await.context(
|
||||||
"could not determine this hive's domain from the daemon — is hive-c0re running \
|
"could not determine this hive's domain from the daemon — is hive-c0re running \
|
||||||
and `services.hyperhive.domain` set? pass --domain to override",
|
and `services.hyperhive.domain` set?",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `wg init` — generate (if absent) the hive's WireGuard key, print its
|
/// `wg init` — generate (if absent) the hive's WireGuard key, print its
|
||||||
/// public key + the nix snippet to enable the mesh, then (best-effort)
|
/// 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
|
/// the `peer-config` block peers paste to federate with this hive, so a
|
||||||
/// fresh setup is one command. The domain comes from `--domain` or the
|
/// fresh setup is one command. The domain comes from the daemon; if it
|
||||||
/// daemon; if neither resolves, the peer block is skipped (init still
|
/// can't be resolved, the peer block is skipped (init still succeeds —
|
||||||
/// succeeds — its core job is enabling the mesh locally).
|
/// its core job is enabling the mesh locally).
|
||||||
async fn wg_init(socket: &Path, address: Option<&str>, domain: Option<&str>) -> Result<()> {
|
async fn wg_init(socket: &Path, address: Option<&str>) -> Result<()> {
|
||||||
use std::os::unix::fs::PermissionsExt as _;
|
use std::os::unix::fs::PermissionsExt as _;
|
||||||
let key_path = Path::new(WG_KEY_PATH);
|
let key_path = Path::new(WG_KEY_PATH);
|
||||||
if key_path.exists() {
|
if key_path.exists() {
|
||||||
|
|
@ -736,13 +718,9 @@ async fn wg_init(socket: &Path, address: Option<&str>, domain: Option<&str>) ->
|
||||||
println!(" }};");
|
println!(" }};");
|
||||||
|
|
||||||
// Also print the block a peer pastes to federate with us (CA + this
|
// Also print the block a peer pastes to federate with us (CA + this
|
||||||
// mesh key) — one-stop setup. Domain from --domain or the daemon;
|
// mesh key) — one-stop setup. Domain comes from the daemon;
|
||||||
// best-effort, so init still succeeds when neither resolves.
|
// best-effort, so init still succeeds when it can't be resolved.
|
||||||
let resolved = match domain {
|
if let Some(d) = query_hive_domain(socket).await {
|
||||||
Some(d) => Some(d.to_owned()),
|
|
||||||
None => query_hive_domain(socket).await,
|
|
||||||
};
|
|
||||||
if let Some(d) = resolved {
|
|
||||||
println!();
|
println!();
|
||||||
peer_config(&d, address, None);
|
peer_config(&d, address, None);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue