feat(#1004,#1006): capability system + read_host_journal / get_host_journal MCP tool

This commit is contained in:
damocles 2026-06-01 20:27:08 +02:00 committed by mara
commit dc8a4e2baf
7 changed files with 345 additions and 3 deletions

View file

@ -428,6 +428,25 @@ pub enum Request {
/// crashed-mid-turn sessions. See
/// `docs/conventions.md::Broker delivery + ack cycle`.
RequeueInflight,
/// *(capability-gated: `read_host_journal`)* Fetch recent lines
/// from the host journal. Filters are all optional; omitting all
/// returns the last `lines` entries from the global journal.
GetHostJournal {
/// Filter to a specific systemd unit (e.g. `hive-c0re.service`).
#[serde(default, skip_serializing_if = "Option::is_none")]
unit: Option<String>,
/// Filter to a specific nspawn container by logical agent name
/// (e.g. `"iris"` → machine `h-iris`). Adds `-M` flag.
#[serde(default, skip_serializing_if = "Option::is_none")]
container: Option<String>,
/// Number of journal lines to return (default 100, max 500).
#[serde(default, skip_serializing_if = "Option::is_none")]
lines: Option<u32>,
/// Minimum syslog priority level: `err`, `warning`, `info`, `debug`.
/// Corresponds to journalctl `-p LEVEL`.
#[serde(default, skip_serializing_if = "Option::is_none")]
priority: Option<String>,
},
// ---- privileged (manager socket only for now) ---------------------------
@ -556,6 +575,10 @@ pub enum Response {
/// `GetLogs` result: journal lines for the requested container.
/// Returned on the manager socket only.
Logs { content: String },
/// `GetHostJournal` result: host journal lines matching the
/// requested filters. Returned on the agent socket when the agent
/// holds the `read_host_journal` capability.
HostJournal { content: String },
/// `ListSchedules` result. Snapshot of every schedule.
/// Returned on the manager socket only.
Schedules { schedules: Vec<WireSchedule> },
@ -821,6 +844,43 @@ impl ToolGroup {
}
}
/// Per-agent capability grants. Stored in `meta/capabilities.json`
/// (same shape as `tool-groups.json`: `{ "alice": ["read_host_journal"] }`).
/// Capabilities control system-level access that hive-c0re enforces
/// at dispatch time; they are orthogonal to tool groups (which control
/// which MCP tools the harness exposes to claude).
///
/// Injected into containers as `HIVE_CAPABILITIES` (comma-separated
/// snake_case) via `meta::render_flake`. The harness reads this to
/// conditionally register capability-gated MCP tools so claude only
/// sees tools it can actually invoke. See `docs/conventions.md::Capabilities`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Capability {
/// Agent can lifecycle-manage the root agent (kill/start/restart)
/// on behalf of the hive when the root has crashed. Named capability
/// for the existing manager privilege — future topology enforcement
/// will gate this via the capability system instead of the hardcoded
/// `container == MANAGER_CONTAINER` check.
ManageRootAgent,
/// Agent can read the full host journal via `GetHostJournal`.
/// hive-c0re checks this capability before running journalctl.
/// MCP tool `get_host_journal` is only registered in the harness
/// when this capability is present.
ReadHostJournal,
}
impl Capability {
/// Canonical snake_case name for this capability (matches serde).
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Self::ManageRootAgent => "manage_root_agent",
Self::ReadHostJournal => "read_host_journal",
}
}
}
/// Schedule row shape on the wire — mirror of
/// `scheduled_prompts::Schedule` but in the public crate so the
/// dashboard and agent surfaces can deserialize without depending