Watch
0
0
Fork
You've already forked hyperhive
0

swarm UI: an alerts page, read by the controller with a Viewer Grafana token from bao

swarm-bao-grafana-controller-token, on the host that runs Grafana, logs in
to Grafana's API over its socket as the built-in admin (the password
swarm-bao-grafana-admin minted, #5138) and keeps a `swarm-controller`
service account that is an enabled Viewer. It mints a token for that
account and writes it to bao at
swarm/controller/swarm-controller/grafana/token under Grafana's leaf. A
ten-minute timer runs it again. The unit keeps a stored token Grafana
accepts. If Grafana refuses the stored token with 401, or bao holds
none, the unit deletes every token of the account and mints a new one.
Grafana's grant gains create/read/update on that leaf, and the
controller's gains read.

swarm-controller serves GET /api/alerts from Grafana's
/api/prometheus/grafana/api/v1/alerts. It reads the token from bao,
keeps it in memory, and re-reads bao once on a 401. The HTTP client has
5s connect and 15s request timeouts. Any failure is a 503 that names its
cause. swarm-ui gets an /alerts page listing them, built the same way as
the hives page.

This is option (c) on #5129, the operator's choice.
This commit is contained in:
atlas 2026-10-15 12:26:06 +02:00 • committed by mara
commit 7d09776742
23 changed files with 888 additions and 37 deletions

View file

@ -0,0 +1,54 @@
//! The Grafana agreement: where `swarm-controller`'s token for Grafana's API
//! lives, and what the object at that path holds.
//!
//! `swarm-bao-grafana-controller-token`, on the host that runs Grafana, mints
//! the token for a Viewer service account and writes it here under Grafana's
//! own certificate; `swarm-controller` reads it under its own. Those two
//! principals' stanzas are the only ones in the store that cover this path.
use serde::{Deserialize, Serialize};
use crate::{
Error,
path::{Kind, principal_prefix},
};
/// The path holding `swarm-controller`'s Grafana token.
///
/// Under [`Kind::Controller`], which no hive's policy reads. The nix half is
/// `swarm-bao.nix`'s `controllerGrafanaTokenLeaf`.
///
/// # Errors
/// Never in practice: the name segment is a constant. The `Result` is
/// [`principal_prefix`]'s.
pub fn controller_token_path() -> Result<String, Error> {
let prefix = principal_prefix(Kind::Controller, crate::matrix::CONTROLLER)?;
Ok(format!("{prefix}/grafana/token"))
}
/// What [`controller_token_path`] holds.
///
/// No `Debug` derive: `value` reads every alert and dashboard in the swarm's
/// Grafana, and a derived `Debug` is one `{:?}` away from a log line.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ControllerToken {
/// The token itself. The minting unit writes it with
/// `bao kv put … value=-`, so this name is load-bearing for a writer this
/// crate does not control.
pub value: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_controllers_grafana_token_is_under_the_controllers_prefix() {
// The literal is what `swarm-bao.nix` grants Grafana's role and the
// controller's, and what the minting unit writes.
assert_eq!(
controller_token_path().expect("a constant segment is legal"),
"swarm/controller/swarm-controller/grafana/token"
);
}
}

View file

@ -5,10 +5,10 @@
//! the rules every path obeys ([`path`]), the translation from this
//! deployment's environment into a logged-in client ([`client`]), and, per kind
//! of secret, the path it lives at together with the fields it holds
//! ([`matrix`], [`queue`], [`mtls`], [`forge`], [`github`], [`acp`], [`ci`],
//! [`review_classifier`], [`builder`], [`claude_login`]). Each of those is a
//! thing the controller and a hive must say identically, so it is said once
//! here.
//! ([`matrix`], [`queue`], [`mtls`], [`forge`], [`github`], [`grafana`],
//! [`acp`], [`ci`], [`review_classifier`], [`builder`], [`claude_login`]).
//! Each of those is a thing the controller and a hive must say identically, so
//! it is said once here.
//!
//! [`policy`] is the same kind of agreement seen from the other side: which of
//! those paths a given principal's own token may read — a hive's, and an
@ -31,6 +31,7 @@ pub mod claude_login;
pub mod client;
pub mod forge;
pub mod github;
pub mod grafana;
pub mod matrix;
pub mod mtls;
pub mod path;