hive-priv: replace json! with typed structs for account sidecar files

This commit is contained in:
damocles 2026-08-13 19:53:34 +02:00 committed by mara
commit fa658567db
3 changed files with 37 additions and 2 deletions

1
Cargo.lock generated
View file

@ -1795,6 +1795,7 @@ dependencies = [
"anyhow", "anyhow",
"hive-priv-sock", "hive-priv-sock",
"libc", "libc",
"serde",
"serde_json", "serde_json",
"tokio", "tokio",
"tracing", "tracing",

View file

@ -11,6 +11,7 @@ workspace = true
anyhow.workspace = true anyhow.workspace = true
hive-priv-sock.workspace = true hive-priv-sock.workspace = true
libc.workspace = true libc.workspace = true
serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
tokio.workspace = true tokio.workspace = true
tracing.workspace = true tracing.workspace = true

View file

@ -27,6 +27,7 @@ use hive_priv_sock::{
NetworkIsolation, PAUSED_MARKER_FILE, PRIV_SOCK, PrivEvent, PrivRequest, PrivResponse, NetworkIsolation, PAUSED_MARKER_FILE, PRIV_SOCK, PrivEvent, PrivRequest, PrivResponse,
PrivStream, PrivStreamLine, SIBLING_CONTAINERS, PrivStream, PrivStreamLine, SIBLING_CONTAINERS,
}; };
use serde::Serialize;
use tokio::io::{AsyncWriteExt, BufReader}; use tokio::io::{AsyncWriteExt, BufReader};
use tokio::net::unix::OwnedWriteHalf; use tokio::net::unix::OwnedWriteHalf;
use tokio::net::{UnixListener, UnixStream}; use tokio::net::{UnixListener, UnixStream};
@ -468,7 +469,10 @@ async fn exec(
// when both `account` and `homeserver` are present; the account // when both `account` and `homeserver` are present; the account
// suffix is already validated above. // suffix is already validated above.
if let (Some(a), Some(hs)) = (account, homeserver) { if let (Some(a), Some(hs)) = (account, homeserver) {
let meta = serde_json::json!({ "homeserver": hs }).to_string(); let meta = serde_json::to_string(&MatrixAccountSidecar {
homeserver: hs.as_str(),
})
.context("serialize matrix account sidecar")?;
write_agent_state_file(agent_name, &format!("matrix-account-{a}.json"), &meta)?; write_agent_state_file(agent_name, &format!("matrix-account-{a}.json"), &meta)?;
} }
Ok(res) Ok(res)
@ -497,7 +501,10 @@ async fn exec(
)?; )?;
// Sidecar carries the base URL — there's no host-side nix config // Sidecar carries the base URL — there's no host-side nix config
// for extra forges, so this is the only place it's persisted. // for extra forges, so this is the only place it's persisted.
let meta = serde_json::json!({ "base_url": base_url }).to_string(); let meta = serde_json::to_string(&ForgeSidecar {
base_url: base_url.as_str(),
})
.context("serialize forge account sidecar")?;
write_agent_state_file(agent_name, &format!("forge-{label}.json"), &meta)?; write_agent_state_file(agent_name, &format!("forge-{label}.json"), &meta)?;
Ok(res) Ok(res)
} }
@ -957,6 +964,32 @@ fn write_state_file_nofollow(dir: &Path, filename: &str, content: &str) -> Resul
Ok(file) Ok(file)
} }
/// Sidecar written alongside an extra matrix account's token
/// (`matrix-account-<name>.json`) so `hive-matrix-mcp` can auto-discover
/// the account's homeserver without a static `matrixAccounts` config
/// entry. Read side: `hive-matrix-mcp/src/accounts.rs`'s
/// `read_account_homeserver` (deliberately reads via a bare
/// `serde_json::Value` rather than this shape — that side treats a
/// malformed/missing sidecar as "skip this account" rather than an
/// error, so it stays loosely typed; this side is the one place the
/// file is written, so it gets the precise shape).
#[derive(Serialize)]
struct MatrixAccountSidecar<'a> {
homeserver: &'a str,
}
/// Sidecar written alongside a dashboard-provisioned extra forge
/// account's token (`forge-<label>.json`) so `hive-forge` can resolve
/// the account's base URL. Read side: `hive-forge/src/client.rs`'s own
/// (separately defined, deserialize-only) `ForgeSidecar` — same field
/// name (`base_url`), no shared crate between `hive-priv` and
/// `hive-forge` to hang a common type off, so the two structs are
/// pinned to the same JSON key by convention, not by the compiler.
#[derive(Serialize)]
struct ForgeSidecar<'a> {
base_url: &'a str,
}
/// Shared helper for `WriteAgentForgeToken` and `WriteAgentMatrixToken`. /// Shared helper for `WriteAgentForgeToken` and `WriteAgentMatrixToken`.
/// Writes `content` to `AGENT_STATE_ROOT/<agent_name>/state/<filename>`, /// Writes `content` to `AGENT_STATE_ROOT/<agent_name>/state/<filename>`,
/// chowns to the agent user (derived from the state dir's existing owner), /// chowns to the agent user (derived from the state dir's existing owner),