Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/swarm-logs/src/main.rs
atlas 44984e0d36 move install_crypto_provider to swarm-secret-client
The rustls provider install is process setup every swarm binary runs, and
swarm-secret-client depends on no other swarm crate, so a binary that only
needs the provider and the store no longer links the queue client.

- swarm-secret-client/src/lib.rs, Cargo.toml: the function, its test, a
  `//!` line on why it lives there, and the rustls dependency; rustls
  `ring` as a dev-dependency feature, so the test builds both providers as
  the binaries do and fails if the install is dropped.
- swarm-queue-client/src/lib.rs, Cargo.toml: function and test removed;
  `connect()` calls the secret client's; rustls dropped (no other use),
  swarm-secret-client added.
- swarm-metrics-auth/Cargo.toml: drop swarm-queue-client, whose only use
  was the provider; the binary no longer links async-nats.
- swarm-logs-auth, swarm-logs, swarm-metrics, swarm-ci-metrics Cargo.toml:
  the dep comment no longer names the provider (they keep the dep for
  agent_token / the OIDC mint).
- Cargo.toml: the rustls comment names the provider's new home.
- 14 call sites (hive-agent, hive-c0re, hive-subagent-mcp x2,
  swarm-builder, swarm-ci-metrics, swarm-claude-login, swarm-controller,
  swarm-logs-auth, swarm-logs, swarm-metrics-auth, swarm-metrics,
  swarm-nats-auth, swarm-review-classifier): one-line path change.
- Cargo.lock: the dependency edges above.
2026-10-18 03:18:43 +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_secret_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");
}
}