hive-sh4re: type infra containers as an InfraContainer enum

Replace the stringly-typed infra-control path with an InfraContainer enum
(Ci/Forge/Gateway/Matrix). The variants are the allowlist: serde rejects any
unknown or unsafe name (hive-c0re has no variant) at the wire boundary, so
hive-priv no longer needs a root-side SIBLING_CONTAINERS.contains() check on
ControlInfraContainer — the type enforces it, and 'the daemon can't stop
itself' is a compile-time guarantee.

- priv_proto: InfraContainer enum; manual Serialize/Deserialize + FromStr +
  unit_name() all key off one mapping, so the wire form ('hive-ci', …) is
  unchanged and there's no drift. ControlInfraContainer.container: String ->
  InfraContainer.
- hive-priv / priv_client / server.rs: thread the enum; scoped_infra returns
  Vec<InfraContainer>; the control handler uses unit_name().
- agent_server: the infra_admin restart gate parses the name via FromStr
  instead of a slice .contains().
- SIBLING_CONTAINERS stays (validate_container_name/_system_name still use it
  for journals / general container validation); a test keeps the enum and the
  slice in lockstep.
This commit is contained in:
atlas 2026-06-19 11:43:52 +02:00 committed by mara
commit cd025b3790
5 changed files with 146 additions and 71 deletions

View file

@ -8,8 +8,8 @@
use anyhow::{Context as _, Result, bail};
use hive_sh4re::priv_proto::{
BindMount, InfraAction, JournalQuery, NetworkIsolation, PRIV_SOCK, PrivEvent, PrivRequest,
PrivResponse, PrivStream,
BindMount, InfraAction, InfraContainer, JournalQuery, NetworkIsolation, PRIV_SOCK, PrivEvent,
PrivRequest, PrivResponse, PrivStream,
};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::UnixStream;
@ -288,21 +288,17 @@ pub async fn restart_matrix_daemon(agent_name: &str) -> Result<()> {
/// re-validates `container` against its root-side allowlist; callers must
/// already have checked the requesting agent holds the `infra_admin`
/// capability.
pub async fn restart_infra_container(container: &str) -> Result<()> {
pub async fn restart_infra_container(container: InfraContainer) -> Result<()> {
control_infra_container(container, InfraAction::Restart).await
}
/// Start / stop / restart a hive infrastructure container (`hive-ci`,
/// `hive-gateway`, `hive-forge`, `hive-matrix`) on the host via `systemctl
/// <action> container@<container>.service`. hive-priv re-validates
/// `container` against its root-side allowlist (`SIBLING_CONTAINERS`). Used
/// by the hive-wide `hivectl stop` / `hivectl start` flow.
pub async fn control_infra_container(container: &str, action: InfraAction) -> Result<()> {
ok(call(&PrivRequest::ControlInfraContainer {
container: container.to_owned(),
action,
})
.await?)
/// <action> container@<container>.service`. The [`InfraContainer`] enum is
/// the allowlist — hive-priv needs no name re-validation. Used by the
/// hive-wide `hivectl stop` / `hivectl start` flow.
pub async fn control_infra_container(container: InfraContainer, action: InfraAction) -> Result<()> {
ok(call(&PrivRequest::ControlInfraContainer { container, action }).await?)
}
/// Ensure the agent's persistent state root is a btrfs subvolume when the