Watch
0
0
Fork
You've already forked hyperhive
0

dashboard: remove the L0GS page and its journal backend

Logs live at swarm level now (VictoriaLogs, read through Grafana
Explore). The hive dashboard's own journal viewer goes, with
everything that existed only to serve it:

- frontend: /logs.html, logs.js, logs.css, the H0M3 "Logs" tile and
  the per-agent menu's "journal logs ->" link. The build-log list
  rules BU1LDS used from logs.css move into builds.css.
- hive-c0re: GET /api/journal/{name} and GET /api/journal-host, and
  priv_client::read_container_journal.
- hive-priv / hive-priv-sock: the ReadContainerJournal request, its
  JournalQuery/JournalOutput wire types and the journalctl -M executor.
- nix: hive-c0re's systemd-journal supplementary group, whose only
  reader was the journal-host handler.
- docs: the L0GS section, the API entry and the security table row.

Closes #5113
Closes #3819
This commit is contained in:
atlas 2026-10-12 00:17:23 +02:00 • committed by mara
commit 884fb3e08b
31 changed files with 192 additions and 1231 deletions

View file

@ -34,7 +34,7 @@ pub const AGENT_PREFIX: &str = "h-";
/// Sibling service containers managed by hive-c0re. This doubles as the
/// authoritative allowlist for the requests that name a container as a
/// string (bind-mount edits, journal reads): only these — or a valid agent
/// string (bind-mount edits): only these — or a valid agent
/// name — are accepted. `hive-c0re` is deliberately absent; so is the
/// gateway, whose nginx is a plain host unit rather than a container.
/// hive-priv re-validates against this list root-side, so it's authoritative
@ -44,7 +44,7 @@ pub const AGENT_PREFIX: &str = "h-";
/// [`InfraContainer`] is what gates
/// [`PrivRequest::ControlInfraContainer`], and it has one variant more than
/// this list (the gateway). Keep the distinction — a name that belongs to
/// no container has no business reaching a `-M` / `nixos-container` call.
/// no container has no business reaching a `nixos-container` call.
pub const SIBLING_CONTAINERS: &[&str] = &["hive-forge", "hive-matrix"];
/// Lifecycle verb for [`PrivRequest::ControlInfraContainer`]. Maps directly
@ -95,11 +95,10 @@ pub enum InfraContainer {
/// other variant is a container.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InfraTarget {
/// An nspawn container, controlled via `container@<name>.service` and
/// readable with `journalctl -M <name>`.
/// An nspawn container, controlled via `container@<name>.service`.
Container(&'static str),
/// A plain host unit. There is no machine to enter: no `-M` journal,
/// no `nixos-container` verb.
/// A plain host unit. There is no machine to enter: no
/// `nixos-container` verb.
HostUnit(&'static str),
}
@ -187,64 +186,6 @@ pub const AGENT_RUNTIME_ROOT: &str = "/run/hyperhive/agents";
/// layout cares about. Root-owned; hive-priv creates it on first use.
pub const MIGRATE_STAGING_ROOT: &str = "/var/lib/hyperhive/migrate-staging";
/// Output format for `ReadContainerJournal`. Maps to journalctl
/// `--output=<...>`. Restricted to the two formats hive callers use so
/// the wire type can't smuggle an arbitrary `--output` value.
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum JournalOutput {
/// `short` — the journalctl default (syslog-style timestamps).
#[default]
Short,
/// `short-iso` — ISO 8601 timestamps.
ShortIso,
}
impl JournalOutput {
/// The string journalctl expects after `--output=`.
#[must_use]
pub fn as_journalctl(self) -> &'static str {
match self {
JournalOutput::Short => "short",
JournalOutput::ShortIso => "short-iso",
}
}
}
/// journalctl knobs for `ReadContainerJournal`. Grouped into one value so
/// the read-journal call chain (`priv_client::read_container_journal` →
/// hive-priv's executor) and its several hive-c0re callers pass a single
/// struct instead of eight positional args that travelled together 1:1.
/// `Default` is the common case (last N lines, short format, no filters);
/// callers fill `lines` and override only the knobs they need via
/// `..Default::default()`.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct JournalQuery {
/// `-n <lines>`.
pub lines: u32,
/// `-b` — restrict to the current boot.
#[serde(default)]
pub boot: bool,
/// `--output=<...>`.
#[serde(default)]
pub output: JournalOutput,
/// `-u <unit>`.
#[serde(default)]
pub unit: Option<String>,
/// `-p <priority>`.
#[serde(default)]
pub priority: Option<String>,
/// `--grep=<regex>`.
#[serde(default)]
pub grep: Option<String>,
/// `--since=<ts>`.
#[serde(default)]
pub since: Option<String>,
/// `--until=<ts>`.
#[serde(default)]
pub until: Option<String>,
}
/// One bind-mount entry for `WriteNspawnFlags`.
/// hive-priv constructs `--bind=<host_path>:<container_path>` (or `--bind-ro=`)
/// and validates both paths before writing the conf file.
@ -342,24 +283,6 @@ pub enum PrivRequest {
/// `nixos-container list`
ListContainers,
// --- Container journal reads ---
/// Read a container's journal via `journalctl -M <container>`.
/// Requires root: the machine-bus transport enters the container's
/// namespace, so this can't run from the unprivileged hive-c0re
/// process. hive-priv validates `container` against the managed-
/// container allowlist, then runs journalctl and returns its output.
///
/// The filters (`unit` / `priority` / `grep` / `since` / `until`)
/// are applied within the already-authorized machine and passed to
/// journalctl as plain argument values; they can't widen access
/// beyond the validated `container`.
ReadContainerJournal {
/// System container name (`h-<agent>` or a sibling service).
container: String,
/// journalctl knobs (see [`JournalQuery`]).
query: JournalQuery,
},
// --- Config file writes ---
/// Update `/etc/nixos-containers/<container>.conf`: strip old network-isolation
/// vars, write `PRIVATE_NETWORK` + bridge settings, and set `EXTRA_NSPAWN_FLAGS`
@ -777,9 +700,8 @@ mod tests {
fn infra_container_enum_matches_sibling_containers() {
// The two lists are the same set *minus the things that aren't
// containers*: every `Container` variant must appear in
// SIBLING_CONTAINERS and vice versa, so a `-M` journal read or a
// `nixos-container` verb can never be pointed at a name that has no
// machine behind it.
// SIBLING_CONTAINERS and vice versa, so a `nixos-container` verb can
// never be pointed at a name that has no machine behind it.
let mut from_enum: Vec<&str> = InfraContainer::ALL
.iter()
.filter_map(|c| match c.target() {