Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/swarm-logs/src/main.rs
atlas 0f53e9be6c swarm-logs: authenticate as the agent with its own queue secret
swarm-logs reads `swarm/agents/<agent>/queue` from the swarm secret
store with the agent's own store certificate (the read hive-agent makes
for its queue connection) and presents it as
`Bearer swarm-agent.<agent>.<secret>`, which the gateway's
swarm-logs-auth checks. On a 401 it reads the secret again and retries
once, so a secret re-minted between the read and the check does not
fail the query.

The OIDC mint against the hive's shared `hive-<h>-agent` client is gone
from swarm-logs, along with the unused `HIVE_AGENT_OIDC_CA_FILE` anchor.
logs.nix now gates on the store address instead of the queue's token
endpoint and wraps the binary with the paths queue-identity.nix gives
the harness; the host forwards the query URL wherever it forwards a
store identity.

The shared client is still delivered into containers; removing that is

Part of #4459.
2026-10-13 08:38:05 +02:00

129 lines
4.9 KiB
Rust

//! `swarm-logs` — an agent's CLI for the swarm log store.
//!
//! An agent can reach VictoriaLogs only through the gateway, and until this
//! existed the way to read it was to hand-roll a `client_credentials` token
//! request and a curl by hand, per query. That is the gap this closes: one
//! verb, a LogsQL string in, matched log lines out, so the answer pipes into
//! `grep` like any other command's.
//!
//! **Read-only and query-only.** It posts to the store's LogsQL query
//! endpoint and nothing else — there is no ingest path here, and no `tail`.
//! Streaming is a different endpoint with a different response shape and is
//! deliberately left for a follow-up rather than folded in.
//!
//! ⚠️ **The route applies no scoping.** `swarm-victorialogs.nix` forwards the
//! query unmodified, so any authenticated caller reads the whole swarm's
//! logs — every hive's, not just its own. That is the rule in force rather
//! than an omission on this binary's part; when read permissions exist they
//! attach at that location, not here.
//!
//! Distinct from `hive-metric`, which is the other half of the same
//! observability surface from an agent's seat: that one *writes* a custom
//! metric, this one *reads* logs.
mod auth;
mod query;
use anyhow::Result;
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(
name = "swarm-logs",
version,
about = "query the swarm's log store from an agent"
)]
struct Cli {
#[command(subcommand)]
command: Verb,
}
#[derive(Subcommand)]
enum Verb {
/// Run a LogsQL query and print the matched log lines.
Query {
/// The LogsQL query. A bare word is a full-text search for it.
///
/// Quote it: LogsQL uses characters the shell also claims, and an
/// unquoted `|` pipes this command into the rest of your query
/// instead of sending it.
logsql: String,
/// Stop after this many records.
///
/// Unset means the store's own default. Worth setting on a broad
/// query — nothing between here and the store narrows one, so a bare
/// word matches across every hive in the swarm.
#[arg(long)]
limit: Option<u64>,
/// What to print for each matched record.
#[arg(long, value_enum, default_value = "message")]
format: query::Format,
},
/// Print this CLI's reference as markdown (source of `docs/tools/swarm-logs-cli.md`).
///
/// No `completions` sibling, unlike `hivectl` and `swarmctl`: those are
/// installed on an operator's interactive host, where a completion script
/// has a shell to be sourced into. This one is run by an agent through a
/// tool call, which has no line editor to complete against — the verb
/// would exist only to be listed.
#[command(hide = true)]
MarkdownDocs,
}
fn main() -> Result<()> {
match Cli::parse().command {
Verb::Query {
logsql,
limit,
format,
} => {
// Config resolved inside this arm and not up front: the two
// verbs below are pure functions of the command tree and must
// keep working on a machine that has no log store wired up at
// all — `markdown-docs` in particular runs in a nix check, where
// no credential exists and none should.
swarm_queue_client::install_crypto_provider();
let cfg = auth::Config::from_env()?;
let stdout = std::io::stdout();
query::run(&cfg, &logsql, limit, format, &mut stdout.lock())
}
Verb::MarkdownDocs => {
// `show_footer(false)`: drop clap-markdown's own fixed
// "generated automatically by..." footer, same as
// hivectl/swarmctl/hive-forge — it's not our prose, and
// write-good.Passive has nothing to check it against.
let options = clap_markdown::MarkdownOptions::new().show_footer(false);
print!("{}", clap_markdown::help_markdown_custom::<Cli>(&options));
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::CommandFactory as _;
/// clap's own consistency check over the command tree — catches a
/// duplicate long flag or a malformed `about` at test time rather than on
/// the first run of a verb nobody exercised.
#[test]
fn the_command_tree_is_well_formed() {
Cli::command().debug_assert();
}
/// The default matters: it is what an agent gets when it pipes into
/// `grep` without reading the flags first.
#[test]
fn query_defaults_to_projecting_the_message() {
let cli = Cli::try_parse_from(["swarm-logs", "query", "a-marker"]).expect("parses");
let Verb::Query { format, limit, .. } = cli.command else {
panic!("expected the query verb");
};
assert_eq!(format, query::Format::Message);
assert_eq!(limit, None, "no client-side default limit is imposed");
}
}