//! The swarm's secret-store client: where a credential lives, and how both ends //! reach it. //! //! The HTTP is [`vaultrs`]'s job. What this crate owns is the *agreements* — //! 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`]). 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 hive's own token may read. It belongs here rather than in the //! controller because the grant and the path are one statement — spelled //! differently they produce a 403 that names neither. //! //! [`client`] is deliberately ignorant of all of it: it moves whatever type a //! caller names, so a second kind of secret is a new module beside [`matrix`] //! and not another field on a struct shared with it. pub mod client; pub mod matrix; pub mod path; pub mod policy; pub mod queue; pub use client::SecretStore; /// What can go wrong between "we have a client certificate" and "we have the /// credential". #[derive(Debug, thiserror::Error)] pub enum Error { /// A name that would have addressed something other than what the caller /// meant. See [`path`]. #[error("{kind} name {value:?} is not a single path segment of [A-Za-z0-9_-]")] PathSegment { /// Which name was rejected. A principal's kind in the singular /// (`agent`, `hive`, `service`, `controller`) when the name addresses /// one, or what the name is to the secret otherwise — `account`, for /// a matrix credential. kind: &'static str, /// The offending value, quoted in the message because the caller /// usually got it from config and needs to see which one. value: String, }, /// A variable the store's address or identity comes from is unset or /// empty. Named rather than defaulted: a wrong store address fails much /// later and much less clearly than a missing one. #[error("{0} is unset or empty")] MissingEnv(&'static str), /// A client-certificate file named by the environment could not be read. #[error("reading {path} (from {var}): {source}")] Identity { /// The variable that named the file. var: &'static str, /// The path it named. path: String, /// The underlying IO failure. source: std::io::Error, }, /// The address would not parse into a URL the client can use. #[error("the store's settings are unusable: {0}")] Settings(String), /// The store refused us, was unreachable, or answered something we could /// not parse. #[error(transparent)] Vault(#[from] Box), /// The client certificate and key did not form a usable identity, or the /// CA bundle did not parse. #[error("building the TLS identity: {0}")] Tls(#[source] reqwest::Error), } impl From for Error { fn from(e: vaultrs::error::ClientError) -> Self { // Boxed because `ClientError` is large enough that carrying it inline // makes every `Result` in the crate pay for the rare arm. Self::Vault(Box::new(e)) } }