Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/swarm-secret-client/src/review_classifier.rs
atlas 2fc63d844b name the mint, not the publisher, as the writer of the client secrets
Finishes the sweep of prose that said the publisher unit writes or carries the OIDC client secrets: rust docs, nix comments and option descriptions, test names and docs.
2026-10-30 16:57:58 +01:00

113 lines
3.9 KiB
Rust

//! The review classifier agreement: where its inference keys, its queue
//! client secret and its forge token live in the store, and what each object
//! holds.
//!
//! The operator writes the keys by hand in the store's UI; the classifier
//! reads them and the client secret under its own certificate, a key once per
//! run. Its read stanza in `swarm-bao.nix` names exactly these three paths. The forge token is `swarm-controller`'s alone: it mints it, and
//! posts the classifier's reviews with it.
use serde::{Deserialize, Serialize};
use crate::{
Error,
path::{Kind, principal_prefix},
};
/// The classifier's name segment under [`Kind::Service`].
const SERVICE: &str = "review-classifier";
/// The path holding the Anthropic API key the classifier runs `claude` with.
///
/// # Errors
/// Never in practice: the name segment is a constant. The `Result` is
/// [`principal_prefix`]'s.
pub fn api_key_path() -> Result<String, Error> {
let prefix = principal_prefix(Kind::Service, SERVICE)?;
Ok(format!("{prefix}/anthropic-api-key"))
}
/// The path holding the provider key the classifier runs an ACP agent with,
/// as a [`crate::acp::ProviderKey`].
///
/// # Errors
/// Never in practice: the name segment is a constant. The `Result` is
/// [`principal_prefix`]'s.
pub fn acp_provider_key_path() -> Result<String, Error> {
let prefix = principal_prefix(Kind::Service, SERVICE)?;
Ok(format!("{prefix}/acp-provider"))
}
/// What [`api_key_path`] holds.
///
/// No `Debug`: `api_key` is a live provider credential.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApiKey {
/// The key itself, under the field name [`crate::acp::ProviderKey`] uses.
/// The operator enters this field name in the store's UI.
pub api_key: String,
}
/// The path holding the token of the classifier's forge account, as a
/// [`crate::forge::Credential`].
///
/// # Errors
/// Never in practice: the name segment is a constant. The `Result` is
/// [`principal_prefix`]'s.
pub fn forge_token_path() -> Result<String, Error> {
let prefix = principal_prefix(Kind::Service, SERVICE)?;
Ok(format!("{prefix}/forge/token"))
}
/// The path holding the queue client secret of the classifier's OIDC client
/// `client_id`, where authelia's mint puts every swarm service's.
///
/// # Errors
/// [`Error::PathSegment`] when `client_id` is not a single path segment.
pub fn client_secret_path(client_id: &str) -> Result<String, Error> {
let prefix = principal_prefix(Kind::Service, client_id)?;
Ok(format!("{prefix}/oidc/client"))
}
/// What [`client_secret_path`] holds.
///
/// No `Debug`: `value` authenticates the classifier at the queue.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ClientSecret {
/// The secret, under the field name authelia's mint writes.
pub value: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_paths_are_the_ones_the_store_grants() {
// Spelled out: the operator types the first into the store's UI, and
// `swarm-bao.nix` grants each literal.
assert_eq!(
api_key_path().expect("a constant segment is legal"),
"swarm/services/review-classifier/anthropic-api-key"
);
assert_eq!(
acp_provider_key_path().expect("a constant segment is legal"),
"swarm/services/review-classifier/acp-provider"
);
assert_eq!(
forge_token_path().expect("a constant segment is legal"),
"swarm/services/review-classifier/forge/token"
);
assert_eq!(
client_secret_path("swarm-review-classifier").expect("a plain id is legal"),
"swarm/services/swarm-review-classifier/oidc/client"
);
}
#[test]
fn the_stored_key_field_is_api_key() {
let stored: ApiKey =
serde_json::from_str(r#"{"api_key":"k"}"#).expect("the documented shape decodes");
assert_eq!(stored.api_key, "k");
}
}