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.
This commit is contained in:
parent
d9c586ef94
commit
44984e0d36
25 changed files with 64 additions and 47 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -4984,7 +4984,6 @@ dependencies = [
|
|||
"clap",
|
||||
"serde",
|
||||
"subtle",
|
||||
"swarm-queue-client",
|
||||
"swarm-secret-client",
|
||||
"tokio",
|
||||
"tracing",
|
||||
|
|
@ -5026,10 +5025,10 @@ dependencies = [
|
|||
"futures-util",
|
||||
"hive-sh4re",
|
||||
"reqwest",
|
||||
"rustls",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum",
|
||||
"swarm-secret-client",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"tracing",
|
||||
|
|
@ -5066,6 +5065,7 @@ dependencies = [
|
|||
"reqwest",
|
||||
"rustify",
|
||||
"rustify_derive",
|
||||
"rustls",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum",
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ async-nats = { version = "0.50", default-features = false, features = [
|
|||
"nkeys",
|
||||
"ring",
|
||||
] }
|
||||
# Named only to install the process-wide provider (`swarm-queue-client`'s
|
||||
# Named only to install the process-wide provider (`swarm-secret-client`'s
|
||||
# `install_crypto_provider`). `aws-lc-rs` is the provider reqwest already
|
||||
# falls back to, so installing it leaves every HTTPS client unchanged.
|
||||
rustls = { version = "0.23", default-features = false, features = [
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ struct Cli {
|
|||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
hive_log::init();
|
||||
swarm_queue_client::install_crypto_provider();
|
||||
swarm_secret_client::install_crypto_provider();
|
||||
|
||||
let cli = Cli::parse();
|
||||
serve_main::<AgentSurface>(&cli.socket, cli.poll_ms).await
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ enum Cmd {
|
|||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
hive_log::init();
|
||||
swarm_queue_client::install_crypto_provider();
|
||||
swarm_secret_client::install_crypto_provider();
|
||||
|
||||
let cli = Cli::parse();
|
||||
match cli.cmd {
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ impl Reader {
|
|||
return None;
|
||||
}
|
||||
};
|
||||
swarm_queue_client::install_crypto_provider();
|
||||
swarm_secret_client::install_crypto_provider();
|
||||
let agent = target.agent.clone();
|
||||
let client = Arc::new(OnceLock::new());
|
||||
tokio::spawn(connect_until_up(Arc::new(target), Arc::clone(&client)));
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ impl Publisher {
|
|||
return None;
|
||||
}
|
||||
};
|
||||
swarm_queue_client::install_crypto_provider();
|
||||
swarm_secret_client::install_crypto_provider();
|
||||
tracing::info!(
|
||||
url = %target.url,
|
||||
stream = %subagent_term::stream_name(&target.agent),
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ const MAX_SLOT_WAIT: Duration = REPLY_DEADLINE
|
|||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
hive_log::init();
|
||||
swarm_queue_client::install_crypto_provider();
|
||||
swarm_secret_client::install_crypto_provider();
|
||||
let var = |name: &str| {
|
||||
let full = format!("{PREFIX}_{name}");
|
||||
std::env::var(&full).with_context(|| format!("{full} is unset"))
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ hive-log.workspace = true
|
|||
reqwest.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
# The OIDC client-credentials mint, and the rustls provider.
|
||||
# The OIDC client-credentials mint.
|
||||
swarm-queue-client.workspace = true
|
||||
# The forge token and client secret paths, and the store login.
|
||||
swarm-secret-client.workspace = true
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ struct Config {
|
|||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
hive_log::init();
|
||||
swarm_queue_client::install_crypto_provider();
|
||||
swarm_secret_client::install_crypto_provider();
|
||||
let var = |name: &str| {
|
||||
let full = format!("{PREFIX}_{name}");
|
||||
std::env::var(&full).with_context(|| format!("{full} is unset"))
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ impl login::CredentialSink for AgentLeaf {
|
|||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
hive_log::init();
|
||||
swarm_queue_client::install_crypto_provider();
|
||||
swarm_secret_client::install_crypto_provider();
|
||||
let var = |name: &str| {
|
||||
let full = format!("{PREFIX}_{name}");
|
||||
std::env::var(&full).with_context(|| format!("{full} is unset"))
|
||||
|
|
|
|||
|
|
@ -3475,7 +3475,7 @@ async fn start_forge(
|
|||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
hive_log::init();
|
||||
swarm_queue_client::install_crypto_provider();
|
||||
swarm_secret_client::install_crypto_provider();
|
||||
|
||||
let path = socket_path();
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ clap.workspace = true
|
|||
# The verdict cache's key: a digest of the bearer, never the bearer.
|
||||
sha2.workspace = true
|
||||
swarm-agent-verify.workspace = true
|
||||
# The agent-token spelling, and the rustls provider the store client needs.
|
||||
# The agent-token spelling.
|
||||
swarm-queue-client.workspace = true
|
||||
swarm-secret-client.workspace = true
|
||||
tokio.workspace = true
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ async fn handle(
|
|||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
swarm_queue_client::install_crypto_provider();
|
||||
swarm_secret_client::install_crypto_provider();
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ path = "src/main.rs"
|
|||
anyhow.workspace = true
|
||||
clap.workspace = true
|
||||
clap-markdown = "0.1"
|
||||
# The agent-token spelling the gateway's responder parses, and the rustls
|
||||
# provider the store client needs.
|
||||
# The agent-token spelling the gateway's responder parses.
|
||||
swarm-queue-client.workspace = true
|
||||
# The read of this agent's own queue secret.
|
||||
swarm-secret-client.workspace = true
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ fn main() -> Result<()> {
|
|||
// 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();
|
||||
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())
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ clap.workspace = true
|
|||
serde.workspace = true
|
||||
# Comparing the presented bearer with the stored one.
|
||||
subtle.workspace = true
|
||||
# Only for the rustls provider the store client needs.
|
||||
swarm-queue-client.workspace = true
|
||||
swarm-secret-client.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ async fn handle(State(responder): State<Arc<Responder<Store>>>, headers: HeaderM
|
|||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
swarm_queue_client::install_crypto_provider();
|
||||
swarm_secret_client::install_crypto_provider();
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ path = "src/main.rs"
|
|||
anyhow.workspace = true
|
||||
clap.workspace = true
|
||||
clap-markdown = "0.1"
|
||||
# The agent-token spelling the gateway's responder parses, and the rustls
|
||||
# provider the store client needs.
|
||||
# The agent-token spelling the gateway's responder parses.
|
||||
swarm-queue-client.workspace = true
|
||||
# The read of this agent's own queue secret.
|
||||
swarm-secret-client.workspace = true
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ fn main() -> Result<()> {
|
|||
};
|
||||
// Config read only for a store verb: `markdown-docs` runs in a nix check,
|
||||
// where no credential exists.
|
||||
swarm_queue_client::install_crypto_provider();
|
||||
swarm_secret_client::install_crypto_provider();
|
||||
let cfg = auth::Config::from_env()?;
|
||||
let stdout = std::io::stdout();
|
||||
query::run(&cfg, &request, &mut stdout.lock())
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ struct Args {
|
|||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
swarm_queue_client::install_crypto_provider();
|
||||
swarm_secret_client::install_crypto_provider();
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
|
|
|
|||
|
|
@ -82,10 +82,11 @@ hive-sh4re = { workspace = true, optional = true }
|
|||
# feature's comment above), and `cargo check -p swarm-queue-client` alone
|
||||
# must not depend on what else is in the build.
|
||||
reqwest = { workspace = true, features = ["blocking"] }
|
||||
rustls.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
strum.workspace = true
|
||||
# The rustls provider `connect` installs.
|
||||
swarm-secret-client.workspace = true
|
||||
# A library, so its errors are a matchable enum rather than an opaque
|
||||
# `anyhow::Error`. The binaries that consume this keep anyhow; `?` converts.
|
||||
thiserror.workspace = true
|
||||
|
|
|
|||
|
|
@ -941,18 +941,6 @@ pub fn max_payload(client: &async_nats::Client) -> usize {
|
|||
client.server_info().max_payload
|
||||
}
|
||||
|
||||
/// Install `aws-lc-rs` as the process-wide rustls crypto provider. Call it
|
||||
/// first thing in `main` of every binary that dials the queue.
|
||||
///
|
||||
/// rustls is built with both `ring` and `aws-lc-rs`; without an installed
|
||||
/// default, the first TLS handshake panics (async-nats builds its config with
|
||||
/// `ClientConfig::builder()`) and the client never connects. `aws-lc-rs`
|
||||
/// because it is what reqwest uses when nothing is installed.
|
||||
pub fn install_crypto_provider() {
|
||||
// `Err` only means a provider is already installed, which is all this needs.
|
||||
let _already_installed = rustls::crypto::aws_lc_rs::default_provider().install_default();
|
||||
}
|
||||
|
||||
/// Connect to the swarm queue, presenting a token on each connection attempt
|
||||
/// and minting a fresh one only when the cached one is near expiry. Reply
|
||||
/// inboxes are under [`client_inbox_prefix`] of `cfg.client_id`.
|
||||
|
|
@ -962,7 +950,7 @@ pub fn install_crypto_provider() {
|
|||
/// token expires, the controller keeps serving, its status data quietly stops
|
||||
/// updating, and nothing says so until someone reads a dashboard.
|
||||
pub async fn connect(cfg: QueueConfig) -> Result<async_nats::Client, Error> {
|
||||
install_crypto_provider();
|
||||
swarm_secret_client::install_crypto_provider();
|
||||
// Trusts `cfg.ca_file` when set (the swarm's own CA, when the token
|
||||
// endpoint is signed by it) — see `build_http_client`. A timeout too,
|
||||
// because this client runs INSIDE the auth callback: a token endpoint
|
||||
|
|
@ -1134,14 +1122,6 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_tls_client_config_builds_once_the_provider_is_installed() {
|
||||
install_crypto_provider();
|
||||
let _config = rustls::ClientConfig::builder()
|
||||
.with_root_certificates(rustls::RootCertStore::empty())
|
||||
.with_no_client_auth();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_debug_print_of_a_config_does_not_carry_a_held_secret() {
|
||||
let cfg = QueueConfig {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ fn main() -> Result<()> {
|
|||
|
||||
async fn serve() -> Result<()> {
|
||||
hive_log::init();
|
||||
swarm_queue_client::install_crypto_provider();
|
||||
swarm_secret_client::install_crypto_provider();
|
||||
let var = |name: &str| {
|
||||
let full = format!("{PREFIX}_{name}");
|
||||
std::env::var(&full).with_context(|| format!("{full} is unset"))
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ reqwest.workspace = true
|
|||
# implement — see `client::WriteAclPolicy`.
|
||||
rustify.workspace = true
|
||||
rustify_derive.workspace = true
|
||||
# `install_crypto_provider`, which the store client (reqwest, via vaultrs)
|
||||
# needs before its first TLS handshake.
|
||||
rustls.workspace = true
|
||||
serde.workspace = true
|
||||
# `read_versioned` decodes the data half of the response vaultrs hands back as
|
||||
# a `serde_json::Value`.
|
||||
|
|
@ -22,3 +25,9 @@ vaultrs.workspace = true
|
|||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# `ring` as well, as every binary that links async-nats builds it: with one
|
||||
# provider compiled in rustls picks it without an install, so the provider
|
||||
# test would pass with `install_crypto_provider` doing nothing.
|
||||
[dev-dependencies]
|
||||
rustls = { workspace = true, features = ["ring"] }
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@
|
|||
//!
|
||||
//! [`mtls`] is the one module about reaching the store rather than about a
|
||||
//! value inside it, and its doc explains why that is not circular.
|
||||
//!
|
||||
//! [`install_crypto_provider`] is process setup, not an agreement. It lives
|
||||
//! here because this crate depends on no other swarm crate, so a binary that
|
||||
//! needs only the provider and the store links nothing else.
|
||||
|
||||
pub mod acp;
|
||||
pub mod builder;
|
||||
|
|
@ -105,3 +109,30 @@ impl From<vaultrs::error::ClientError> for Error {
|
|||
Self::Vault(Box::new(e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Install `aws-lc-rs` as the process-wide rustls crypto provider. Call it
|
||||
/// first thing in `main` of every binary that dials the queue or reaches the
|
||||
/// secret store over TLS.
|
||||
///
|
||||
/// rustls is built with both `ring` and `aws-lc-rs`; without an installed
|
||||
/// default, the first TLS handshake panics. On the queue side async-nats
|
||||
/// builds its config with `ClientConfig::builder()`, so the client never
|
||||
/// connects. On the store side the client is reqwest, via vaultrs.
|
||||
/// `aws-lc-rs` because it is what reqwest uses when nothing is installed.
|
||||
pub fn install_crypto_provider() {
|
||||
// `Err` only means a provider is already installed, which is all this needs.
|
||||
let _already_installed = rustls::crypto::aws_lc_rs::default_provider().install_default();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn a_tls_client_config_builds_once_the_provider_is_installed() {
|
||||
install_crypto_provider();
|
||||
let _config = rustls::ClientConfig::builder()
|
||||
.with_root_certificates(rustls::RootCertStore::empty())
|
||||
.with_no_client_auth();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue