Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/swarm-secret-client/src/attic.rs
atlas 1e5070796d copy the cache's pull facts to each hive and create them in the in-swarm cache
the in-swarm cache stores its address, name and public key beside its tokens, create-only. swarm-controller copies the four cache facts to swarm/hives/<hive>/attic/* every five minutes, with read on the sources and write on the copies. part 1 of 3: nothing reads the copies until the hive reader and probe land.
2026-10-28 01:19:56 +01:00

104 lines
3.3 KiB
Rust

//! The binary cache agreement: the four facts a hive needs to pull from the
//! swarm's cache, where the cache's operator keeps them, and where the
//! controller copies them for each hive.
//!
//! The cache's own unit (or, for an external cache, the operator) writes them
//! under `swarm/services/attic/`. No hive may read that prefix, so
//! `swarm-controller` copies each leaf to `swarm/hives/<hive>/attic/<leaf>`,
//! which the hive's own `swarm/hives/<hive>/*` stanza reads. The nix half of
//! the source paths is `swarm-bao.nix`'s `atticFactLeaves`.
use serde::{Deserialize, Serialize};
use crate::{
Error,
path::{Kind, principal_prefix},
};
/// One of the facts a hive pulls with.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Leaf {
/// The read-only token, as a basic-auth password.
PullToken,
/// The cache server's base URL, scheme included and no trailing slash.
Url,
/// The cache's name on that server.
Cache,
/// The cache's public signing key, `<name>:<base64>`.
PublicKey,
}
impl Leaf {
/// Every leaf, in the order the controller copies them.
pub const ALL: [Leaf; 4] = [Leaf::PullToken, Leaf::Url, Leaf::Cache, Leaf::PublicKey];
/// The last path segment, under both prefixes.
#[must_use]
pub fn name(self) -> &'static str {
match self {
Leaf::PullToken => "pull-token",
Leaf::Url => "url",
Leaf::Cache => "cache",
Leaf::PublicKey => "public-key",
}
}
}
/// Where the cache's operator keeps `leaf`.
///
/// # Errors
/// Never in practice: the name segments are constants. The `Result` is
/// [`principal_prefix`]'s.
pub fn service_path(leaf: Leaf) -> Result<String, Error> {
let prefix = principal_prefix(Kind::Service, "attic")?;
Ok(format!("{prefix}/{}", leaf.name()))
}
/// Where `hive` reads its copy of `leaf`.
///
/// # Errors
/// [`Error::PathSegment`] when `hive` contains anything but `[A-Za-z0-9_-]`,
/// which keeps one hive's name from addressing another hive's secret.
pub fn hive_path(hive: &str, leaf: Leaf) -> Result<String, Error> {
let prefix = principal_prefix(Kind::Hive, hive)?;
Ok(format!("{prefix}/attic/{}", leaf.name()))
}
/// What every leaf holds: one string under the field `bao kv get
/// -field=value` reads.
///
/// No `Debug`: for [`Leaf::PullToken`] `value` reads from the cache.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Fact {
/// The fact itself.
pub value: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_paths_are_the_ones_the_store_grants() {
// Spelled out: `swarm-bao.nix` grants each literal.
let sources = Leaf::ALL.map(|l| service_path(l).expect("constant segments"));
assert_eq!(
sources,
[
"swarm/services/attic/pull-token",
"swarm/services/attic/url",
"swarm/services/attic/cache",
"swarm/services/attic/public-key",
]
);
assert_eq!(
hive_path("alpha", Leaf::PullToken).expect("a plain name"),
"swarm/hives/alpha/attic/pull-token"
);
}
#[test]
fn a_hive_name_cannot_address_another_hives_copy() {
assert!(hive_path("alpha/../beta", Leaf::Url).is_err());
}
}