pub struct ManifestCache {
rollouts_dir: PathBuf,
fleet_dir: PathBuf,
trust_path: PathBuf,
freshness_window: Duration,
}Fields§
§rollouts_dir: PathBuf§fleet_dir: PathBuf§trust_path: PathBuf§freshness_window: DurationImplementations§
Source§impl ManifestCache
impl ManifestCache
pub fn new(state_dir: &Path, trust_path: &Path) -> Self
Sourcepub fn new_with_freshness(
state_dir: &Path,
trust_path: &Path,
freshness_window: Duration,
) -> Self
pub fn new_with_freshness( state_dir: &Path, trust_path: &Path, freshness_window: Duration, ) -> Self
Tunable-freshness constructor. Tests with fixed-signedAt fixtures
pass a longer window so old signed bytes still verify; production
uses Self::new which pins DEFAULT_FRESHNESS_WINDOW_SECS.
Sourcepub fn new_default(state_dir: &Path) -> Self
pub fn new_default(state_dir: &Path) -> Self
Construct a ManifestCache rooted under state_dir/{rollouts,fleet}/
and pointed at the conventional DEFAULT_TRUST_PATH. The longpoll
worker uses this; tests use Self::new to inject a tempdir-rooted
trust file.
fn manifest_path(&self, rollout_id: &str) -> PathBuf
fn signature_path(&self, rollout_id: &str) -> PathBuf
fn fleet_path(&self) -> PathBuf
fn fleet_sig_path(&self) -> PathBuf
Sourcepub fn read_cached_bytes(&self, rollout_id: &str) -> Option<(Vec<u8>, Vec<u8>)>
pub fn read_cached_bytes(&self, rollout_id: &str) -> Option<(Vec<u8>, Vec<u8>)>
Reads (manifest, sig) bytes if both exist; does NOT verify.
fn load_trust_roots( &self, now: DateTime<Utc>, ) -> Result<(Vec<TrustedPubkey>, Option<DateTime<Utc>>)>
Sourcefn validate_rollout_id_for_path(rollout_id: &str) -> Result<(), ManifestError>
fn validate_rollout_id_for_path(rollout_id: &str) -> Result<(), ManifestError>
Path-traversal sanity check on the rollout_id string before it is
embedded in any filesystem path. Mirrors the CP route validator’s
shape (RFC-0008 §6.3 canonical format "channel@channel_ref"); both
layers refuse / and .. so neither side can be coerced into
reading a file outside its rollouts directory.
fn verify_bytes( &self, manifest_bytes: &[u8], signature_bytes: &[u8], advertised_rollout_id: &str, ) -> Result<VerifiedRolloutManifest, ManifestError>
Sourcefn assert_rollout_id_matches(
manifest: &RolloutManifest,
advertised_rollout_id: &str,
) -> Result<(), ManifestError>
fn assert_rollout_id_matches( manifest: &RolloutManifest, advertised_rollout_id: &str, ) -> Result<(), ManifestError>
Discriminator per RFC-0008 §6.3: the canonical identity is
"{channel}@{channel_ref}" derived from the parsed manifest’s
fields. Defense-in-depth that the advertised id matches the
manifest’s actual identity; the signature verify above already
authenticates the bytes, so this catches filename / advertised-id
substitution attacks where attacker-signed bytes carrying a
different (channel, channel_ref) arrive at a path claiming the
canonical id of a different rollout.
fn assert_membership( manifest: &RolloutManifest, hostname: &str, wave_index: u32, ) -> Result<(), ManifestError>
Sourcefn assert_target_closure(
manifest: &RolloutManifest,
hostname: &str,
expected_target_closure: &str,
) -> Result<(), ManifestError>
fn assert_target_closure( manifest: &RolloutManifest, hostname: &str, expected_target_closure: &str, ) -> Result<(), ManifestError>
RFC-0005 §4.1 advisory-payload contract: agent acts on a Dispatch
only if the dispatched target_closure matches the manifest’s
declared target_closure for this host. Pure function; tested in
isolation. The dispatch path’s canonical entry composes this with
[fetch_or_load] via [ensure_for_dispatch].
fn write_cache( &self, rollout_id: &str, manifest_bytes: &[u8], sig_bytes: &[u8], ) -> Result<()>
Sourcepub async fn fetch_or_load(
&self,
client: &Client,
cp_url: &str,
rollout_id: &str,
) -> Result<VerifiedRolloutManifest, ManifestError>
pub async fn fetch_or_load( &self, client: &Client, cp_url: &str, rollout_id: &str, ) -> Result<VerifiedRolloutManifest, ManifestError>
Disk-cache hit re-verifies bytes (defense in depth); miss OR cache
verify-failure fetches from CP, verifies, writes through. Public so
the periodic manifest_poll worker can fetch rollouts independently
of any dispatch arrival (agent-side feed).
LOADBEARING: verify-failure falls through to fetch. Returning the verify error directly would leave a stale cached manifest permanently stuck on freshness/signature errors without ever attempting a CP refresh.
Sourcepub async fn ensure(
&self,
client: &Client,
cp_url: &str,
rollout_id: &str,
hostname: &str,
wave_index: u32,
) -> Result<VerifiedRolloutManifest, ManifestError>
pub async fn ensure( &self, client: &Client, cp_url: &str, rollout_id: &str, hostname: &str, wave_index: u32, ) -> Result<VerifiedRolloutManifest, ManifestError>
Fetch + verify a manifest, then assert (hostname, wave_index)
membership. Used by callers that need the explicit wave-index
sanity check; the dispatch path uses Self::ensure_for_dispatch
instead.
Sourcepub async fn ensure_for_dispatch(
&self,
client: &Client,
cp_url: &str,
rollout_id: &str,
hostname: &str,
expected_target_closure: &str,
) -> Result<VerifiedRolloutManifest, ManifestError>
pub async fn ensure_for_dispatch( &self, client: &Client, cp_url: &str, rollout_id: &str, hostname: &str, expected_target_closure: &str, ) -> Result<VerifiedRolloutManifest, ManifestError>
Canonical dispatch entry: fetch + verify the manifest, then assert
the dispatched target_closure matches the manifest’s declaration
for this host (RFC-0005 §4.1). The longpoll worker’s only path
from a DispatchResponse into the reducer.
fn read_cached_fleet_bytes(&self) -> Option<(Vec<u8>, Vec<u8>)>
fn write_fleet_cache( &self, artifact_bytes: &[u8], sig_bytes: &[u8], ) -> Result<()>
fn verify_fleet_bytes( &self, artifact_bytes: &[u8], signature_bytes: &[u8], ) -> Result<VerifiedFleet, ManifestError>
Sourcepub async fn fetch_or_load_fleet(
&self,
client: &Client,
cp_url: &str,
) -> Result<(VerifiedFleet, String), ManifestError>
pub async fn fetch_or_load_fleet( &self, client: &Client, cp_url: &str, ) -> Result<(VerifiedFleet, String), ManifestError>
Disk-cache hit re-verifies bytes; miss OR cache verify-failure
fetches /v1/fleet.resolved + /sig from CP, verifies, writes
through. Returns the verified struct paired with
canonical_hash_from_bytes(artifact_bytes) so the periodic
manifest_poll worker can cross-check each fetched rollout’s
fleet_resolved_hash against this anchor (per the architect’s
amendment to the d010-feed plan, restated under Option C:
discriminator moves from this function’s signature into the
worker’s tick logic as a cross-consistency check between the
two signed sources).
LOADBEARING: verify-failure falls through to fetch. Returning
the cache’s verify Err without attempting CP refresh would
leave an aged-out cached manifest permanently stuck — every
manifest_poll tick re-verifying the same stale bytes,
returning Stale, and the reducer’s advance_tick pass-gate
would have no fresh manifest to consult.