use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use crate::install::VerifyError;
use crate::layer::{LayerId, Line};
use crate::verify::{dsse_sign_typed, dsse_verify_typed};
pub const LINE_STATUS_PAYLOAD_TYPE: &str = "application/vnd.pulseengine.varve.line-status.v1+json";
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct KnownProblem {
pub id: String,
pub title: String,
pub severity: String,
pub affected: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub workaround: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub detection: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub mitigation: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct LineStatus {
pub line: String,
pub counter: u64,
#[serde(rename = "issued-at")]
pub issued_at: String,
#[serde(
rename = "support-until",
skip_serializing_if = "Option::is_none",
default
)]
pub support_until: Option<String>,
#[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
pub yanked: BTreeMap<String, String>,
#[serde(
rename = "known-problems",
skip_serializing_if = "Vec::is_empty",
default
)]
pub known_problems: Vec<KnownProblem>,
}
#[derive(Debug, thiserror::Error)]
pub enum LineStatusError {
#[error("line-status envelope rejected: {0}")]
Envelope(String),
#[error("cannot sign the line-status document: {0}")]
Sign(String),
#[error("line-status payload is not valid: {0}")]
Payload(String),
#[error("line-status covers line {got} but line {expected} was requested")]
LineMismatch { expected: String, got: String },
#[error(
"refusing stale line-status document for {line}: presented counter {presented}, cached {cached}"
)]
Stale {
line: String,
presented: u64,
cached: u64,
},
#[error(
"{what} names layer '{id}', which is not a layer of line {line} ({reason}) — `varve \
status` matches layer ids exactly, so this entry would never fire for any installed \
layer; fix the id and re-sign the document"
)]
DeadReference {
what: String,
id: String,
line: String,
reason: String,
},
#[error(
"{layout} is not an OCI image layout (it has no index.json) — point --layout at the \
directory `varve deposit --out` produced"
)]
NotALayout { layout: String },
#[error("io error at {path}")]
Io {
path: String,
#[source]
source: std::io::Error,
},
}
impl LineStatus {
pub fn verify_and_parse(
envelope: &[u8],
root_public_key: &[u8],
) -> Result<Self, LineStatusError> {
if let Ok(text) = std::str::from_utf8(envelope)
&& wsc::dsse::DsseEnvelope::from_json(text).is_err()
{
return Err(not_an_envelope(text));
}
let payload = dsse_verify_typed(envelope, LINE_STATUS_PAYLOAD_TYPE, root_public_key)
.map_err(|VerifyError(msg)| {
let hint = if msg.contains("does not verify") {
" (is the document signed by THIS realm's root? `varve pubkey <key>` \
prints the public half a signature verifies against)"
} else {
""
};
LineStatusError::Envelope(format!("{msg}{hint}"))
})?;
serde_json::from_slice(&payload).map_err(|e| LineStatusError::Payload(e.to_string()))
}
pub fn sign(&self, secret_key: &[u8], key_id: &str) -> Result<String, LineStatusError> {
self.check_layer_refs()?;
let payload = serde_json::to_vec_pretty(self).expect("status serializes");
dsse_sign_typed(&payload, LINE_STATUS_PAYLOAD_TYPE, secret_key, key_id)
.map_err(|VerifyError(msg)| LineStatusError::Sign(msg))
}
pub fn check_layer_refs(&self) -> Result<(), LineStatusError> {
let line: Line = self.line.parse().map_err(|e| {
LineStatusError::Payload(format!("'{}' is not a YYYY.MM line: {e}", self.line))
})?;
let check = |what: String, id: &str| -> Result<(), LineStatusError> {
let dead = |reason: String| LineStatusError::DeadReference {
what: what.clone(),
id: id.to_string(),
line: self.line.clone(),
reason,
};
match id.parse::<LayerId>() {
Ok(layer) if layer.line() == &line => Ok(()),
Ok(layer) => Err(dead(format!("it belongs to line {}", layer.line()))),
Err(e) => Err(dead(e.to_string())),
}
};
for id in self.yanked.keys() {
check("the yank entry".to_string(), id)?;
}
for kp in &self.known_problems {
for id in &kp.affected {
check(format!("known problem '{}'", kp.id), id)?;
}
}
Ok(())
}
pub fn report_for(&self, layer: &LayerId) -> LayerStatusReport {
let name = layer.to_string();
let problems: Vec<&KnownProblem> = self
.known_problems
.iter()
.filter(|kp| kp.affected.iter().any(|a| a == &name))
.collect();
LayerStatusReport {
yanked_reason: self.yanked.get(&name).cloned(),
support_until: self.support_until.clone(),
problems_total: problems.len(),
problems_with_workaround: problems.iter().filter(|kp| kp.workaround.is_some()).count(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LayerStatusReport {
pub yanked_reason: Option<String>,
pub support_until: Option<String>,
pub problems_total: usize,
pub problems_with_workaround: usize,
}
#[derive(Debug)]
pub struct StatusCache {
dir: PathBuf,
}
impl StatusCache {
pub fn at_root(root: &Path) -> Self {
StatusCache {
dir: root.join("state").join("line-status"),
}
}
pub fn update(
&self,
line: &Line,
envelope: &[u8],
parsed: &LineStatus,
) -> Result<(), LineStatusError> {
if let Some(cached) = self.load_parsed(line)?
&& parsed.counter < cached.counter
{
return Err(LineStatusError::Stale {
line: line.to_string(),
presented: parsed.counter,
cached: cached.counter,
});
}
let io = |path: &Path, source: std::io::Error| LineStatusError::Io {
path: path.display().to_string(),
source,
};
std::fs::create_dir_all(&self.dir).map_err(|e| io(&self.dir, e))?;
let path = self.envelope_path(line);
std::fs::write(&path, envelope).map_err(|e| io(&path, e))?;
Ok(())
}
pub fn envelope_bytes(&self, line: &Line) -> Result<Option<Vec<u8>>, LineStatusError> {
let path = self.envelope_path(line);
match std::fs::read(&path) {
Ok(bytes) => Ok(Some(bytes)),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(source) => Err(LineStatusError::Io {
path: path.display().to_string(),
source,
}),
}
}
pub fn load(
&self,
line: &Line,
root_public_key: &[u8],
) -> Result<Option<LineStatus>, LineStatusError> {
let path = self.envelope_path(line);
match std::fs::read(&path) {
Ok(bytes) => Ok(Some(LineStatus::verify_and_parse(&bytes, root_public_key)?)),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(source) => Err(LineStatusError::Io {
path: path.display().to_string(),
source,
}),
}
}
fn load_parsed(&self, line: &Line) -> Result<Option<LineStatus>, LineStatusError> {
let path = self.envelope_path(line);
match std::fs::read(&path) {
Ok(bytes) => {
let text = std::str::from_utf8(&bytes)
.map_err(|_| LineStatusError::Payload("cache is not UTF-8".into()))?;
let env = wsc::dsse::DsseEnvelope::from_json(text)
.map_err(|e| LineStatusError::Payload(e.to_string()))?;
let payload = env
.payload_bytes()
.map_err(|e| LineStatusError::Payload(e.to_string()))?;
Ok(Some(
serde_json::from_slice(&payload)
.map_err(|e| LineStatusError::Payload(e.to_string()))?,
))
}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(source) => Err(LineStatusError::Io {
path: path.display().to_string(),
source,
}),
}
}
fn envelope_path(&self, line: &Line) -> PathBuf {
self.dir.join(format!("{line}.dsse.json"))
}
}
pub const LINE_STATUS_ARTIFACT_TYPE: &str = LINE_STATUS_PAYLOAD_TYPE;
pub const ANN_LINE: &str = "eu.pulseengine.varve.status-line";
pub fn attach_to_layout(
layout: &Path,
line: &Line,
envelope: &[u8],
) -> Result<(), LineStatusError> {
let io = |path: &Path, source: std::io::Error| LineStatusError::Io {
path: path.display().to_string(),
source,
};
let digest = crate::store::manifest_digest(envelope);
let hex = digest.strip_prefix("sha256:").expect("digest shape");
let blob_dir = layout.join("blobs").join("sha256");
std::fs::create_dir_all(&blob_dir).map_err(|e| io(&blob_dir, e))?;
let blob_path = blob_dir.join(hex);
std::fs::write(&blob_path, envelope).map_err(|e| io(&blob_path, e))?;
let index_path = layout.join("index.json");
let mut index: serde_json::Value =
serde_json::from_slice(&std::fs::read(&index_path).map_err(|e| io(&index_path, e))?)
.map_err(|e| LineStatusError::Payload(format!("index.json: {e}")))?;
let entries = index["manifests"]
.as_array_mut()
.ok_or_else(|| LineStatusError::Payload("index.json has no manifests array".into()))?;
let line_name = line.to_string();
entries.retain(|e| {
!(e["artifactType"] == LINE_STATUS_ARTIFACT_TYPE
&& e["annotations"][ANN_LINE] == *line_name)
});
entries.push(serde_json::json!({
"mediaType": "application/json",
"artifactType": LINE_STATUS_ARTIFACT_TYPE,
"digest": digest,
"size": envelope.len(),
"annotations": { ANN_LINE: line_name }
}));
std::fs::write(
&index_path,
serde_json::to_vec_pretty(&index).expect("index serializes"),
)
.map_err(|e| io(&index_path, e))?;
Ok(())
}
pub fn cache_baseline_from_source(
source: &dyn crate::source::LayerSource,
layer: &crate::source::LayerRef,
line: &Line,
root_pk: &[u8],
store_root: &Path,
) -> Result<Option<u64>, LineStatusError> {
let envelope = match source
.fetch_line_status(layer)
.map_err(|e| LineStatusError::Payload(format!("fetching baseline line-status: {e}")))?
{
Some(bytes) => bytes,
None => return Ok(None),
};
let doc = LineStatus::verify_and_parse(&envelope, root_pk)?;
if doc.line != line.to_string() {
return Err(LineStatusError::LineMismatch {
expected: line.to_string(),
got: doc.line,
});
}
let counter = doc.counter;
StatusCache::at_root(store_root).update(line, &envelope, &doc)?;
Ok(Some(counter))
}
pub fn attach_envelope_to_layout(
layout: &Path,
envelope: &[u8],
) -> Result<(Line, u64), LineStatusError> {
if !layout.join("index.json").is_file() {
return Err(LineStatusError::NotALayout {
layout: layout.display().to_string(),
});
}
let text = std::str::from_utf8(envelope)
.map_err(|e| LineStatusError::Payload(format!("envelope is not utf-8: {e}")))?;
let env = wsc::dsse::DsseEnvelope::from_json(text).map_err(|_| not_an_envelope(text))?;
let payload = env
.payload_bytes()
.map_err(|e| LineStatusError::Payload(format!("envelope payload: {e}")))?;
let doc: LineStatus = serde_json::from_slice(&payload)
.map_err(|e| LineStatusError::Payload(format!("status document: {e}")))?;
let line: Line = doc
.line
.parse()
.map_err(|e| LineStatusError::Payload(format!("status line '{}': {e}", doc.line)))?;
doc.check_layer_refs()?;
if let Some(existing) = read_any_from_layout(layout)?
&& let Ok(prev) = parse_unverified(&existing)
&& prev.line == doc.line
&& doc.counter < prev.counter
{
return Err(LineStatusError::Stale {
line: doc.line.clone(),
presented: doc.counter,
cached: prev.counter,
});
}
if let Some(layout_line) = layout_line(layout)
&& layout_line != line.to_string()
{
return Err(LineStatusError::LineMismatch {
expected: layout_line,
got: line.to_string(),
});
}
attach_to_layout(layout, &line, envelope)?;
Ok((line, doc.counter))
}
fn not_an_envelope(text: &str) -> LineStatusError {
if serde_json::from_str::<LineStatus>(text).is_ok() {
LineStatusError::Payload(
"this is the UNSIGNED status document, not a signed envelope — sign it first \
(`varve sign-status --file <doc> --key <key> --out <envelope>`) and pass the \
envelope"
.into(),
)
} else {
LineStatusError::Payload(
"not a DSSE envelope — expected the signed output of `varve sign-status`".into(),
)
}
}
fn parse_unverified(envelope: &[u8]) -> Result<LineStatus, LineStatusError> {
let text = std::str::from_utf8(envelope)
.map_err(|e| LineStatusError::Payload(format!("envelope is not utf-8: {e}")))?;
let env = wsc::dsse::DsseEnvelope::from_json(text).map_err(|_| not_an_envelope(text))?;
let payload = env
.payload_bytes()
.map_err(|e| LineStatusError::Payload(format!("envelope payload: {e}")))?;
serde_json::from_slice(&payload)
.map_err(|e| LineStatusError::Payload(format!("status document: {e}")))
}
pub(crate) fn layout_line(layout: &Path) -> Option<String> {
let index: serde_json::Value =
serde_json::from_slice(&std::fs::read(layout.join("index.json")).ok()?).ok()?;
for m in index["manifests"].as_array()? {
let digest = m["digest"].as_str()?.replace(':', "-");
let blob = layout
.join("blobs")
.join("sha256")
.join(digest.trim_start_matches("sha256-"));
let Ok(bytes) = std::fs::read(&blob) else {
continue;
};
let Ok(text) = std::str::from_utf8(&bytes) else {
continue;
};
let Ok(env) = wsc::dsse::DsseEnvelope::from_json(text) else {
continue;
};
let Ok(payload) = env.payload_bytes() else {
continue;
};
let Ok(doc) = serde_json::from_slice::<serde_json::Value>(&payload) else {
continue;
};
if let Some(line) = doc["annotations"]["eu.pulseengine.varve.line"].as_str() {
return Some(line.to_string());
}
}
None
}
pub fn read_any_from_layout(layout: &Path) -> Result<Option<Vec<u8>>, LineStatusError> {
let index_path = layout.join("index.json");
let bytes = match std::fs::read(&index_path) {
Ok(bytes) => bytes,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None),
Err(source) => {
return Err(LineStatusError::Io {
path: index_path.display().to_string(),
source,
});
}
};
let index: serde_json::Value = serde_json::from_slice(&bytes)
.map_err(|e| LineStatusError::Payload(format!("index.json: {e}")))?;
let Some(entry) = index["manifests"].as_array().and_then(|entries| {
entries
.iter()
.find(|e| e["artifactType"] == LINE_STATUS_ARTIFACT_TYPE)
}) else {
return Ok(None);
};
let digest = entry["digest"]
.as_str()
.ok_or_else(|| LineStatusError::Payload("status entry has no digest".into()))?;
let hex = digest.strip_prefix("sha256:").unwrap_or(digest);
let blob_path = layout.join("blobs").join("sha256").join(hex);
std::fs::read(&blob_path)
.map(Some)
.map_err(|source| LineStatusError::Io {
path: blob_path.display().to_string(),
source,
})
}
pub fn read_from_layout(layout: &Path, line: &Line) -> Result<Option<Vec<u8>>, LineStatusError> {
let index_path = layout.join("index.json");
let bytes = match std::fs::read(&index_path) {
Ok(bytes) => bytes,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None),
Err(source) => {
return Err(LineStatusError::Io {
path: index_path.display().to_string(),
source,
});
}
};
let index: serde_json::Value = serde_json::from_slice(&bytes)
.map_err(|e| LineStatusError::Payload(format!("index.json: {e}")))?;
let line_name = line.to_string();
let Some(entry) = index["manifests"].as_array().and_then(|entries| {
entries.iter().find(|e| {
e["artifactType"] == LINE_STATUS_ARTIFACT_TYPE
&& e["annotations"][ANN_LINE] == *line_name
})
}) else {
return Ok(None);
};
let digest = entry["digest"]
.as_str()
.ok_or_else(|| LineStatusError::Payload("status entry has no digest".into()))?;
let hex = digest.strip_prefix("sha256:").unwrap_or(digest);
let blob_path = layout.join("blobs").join("sha256").join(hex);
std::fs::read(&blob_path)
.map(Some)
.map_err(|source| LineStatusError::Io {
path: blob_path.display().to_string(),
source,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::verify::generate_root_keypair;
fn status(counter: u64) -> LineStatus {
LineStatus {
line: "2026.07".into(),
counter,
issued_at: "2026-08-07T00:00:00Z".into(),
support_until: Some("2028-07-31".into()),
yanked: BTreeMap::from([(
"2026.07.0".to_string(),
"CVE-2026-0001 in synth".to_string(),
)]),
known_problems: vec![
KnownProblem {
id: "KP-1".into(),
title: "synth mla fusion regresses flat_flight".into(),
severity: "medium".into(),
affected: vec!["2026.07.0".into()],
workaround: Some("disable mla fusion".into()),
detection: None,
mitigation: None,
},
KnownProblem {
id: "KP-2".into(),
title: "witness truth-table gap on nested variants".into(),
severity: "high".into(),
affected: vec!["2026.07.0".into(), "2026.07.1".into()],
workaround: None,
detection: Some("witness gap rows non-empty".into()),
mitigation: None,
},
],
}
}
#[test]
fn a_signed_status_document_round_trips() {
let (sk, pk) = generate_root_keypair();
let envelope = status(1).sign(&sk, "varve-root-1").unwrap();
let parsed = LineStatus::verify_and_parse(envelope.as_bytes(), &pk).unwrap();
assert_eq!(parsed, status(1));
}
#[test]
fn a_layer_manifest_envelope_cannot_pose_as_a_status_document() {
let (sk, pk) = generate_root_keypair();
let manifest = crate::manifest::fixtures::manifest(
"2026.07.0",
"qualified",
1,
"2026-08-07T00:00:00Z",
);
let envelope = crate::verify::sign_layer_manifest(&manifest, &sk, "varve-root-1").unwrap();
let err = LineStatus::verify_and_parse(envelope.as_bytes(), &pk).unwrap_err();
assert!(err.to_string().contains("payload type"), "got: {err}");
}
#[test]
fn the_report_names_yank_support_window_and_problem_counts() {
let doc = status(1);
let report = doc.report_for(&"2026.07.0".parse().unwrap());
assert_eq!(
report.yanked_reason.as_deref(),
Some("CVE-2026-0001 in synth")
);
assert_eq!(report.support_until.as_deref(), Some("2028-07-31"));
assert_eq!(report.problems_total, 2);
assert_eq!(report.problems_with_workaround, 1);
let clean = doc.report_for(&"2026.07.2".parse().unwrap());
assert_eq!(clean.yanked_reason, None);
assert_eq!(clean.problems_total, 0);
}
#[test]
fn attaching_status_to_a_layout_leaves_every_layer_blob_untouched() {
use crate::deposit::{DepositSpec, DepositTool, deposit};
let (sk, pk) = generate_root_keypair();
let tmp = tempfile::tempdir().unwrap();
let dest = tmp.path().join("layout");
let spec = DepositSpec {
includes: Vec::new(),
layer: "2026.07.0".parse().unwrap(),
channel: "qualified".into(),
counter: 1,
issued_at: "2026-08-07T00:00:00Z".into(),
tools: vec![DepositTool {
name: "synth".into(),
version: "1".into(),
platform: None,
bytes: b"t".to_vec(),
source: None,
runner: None,
kind: None,
sdk_prefix: None,
}],
};
let outcome = deposit(&spec, &sk, "k", &dest).unwrap();
let blob_dir = dest.join("blobs/sha256");
let before: std::collections::BTreeMap<String, Vec<u8>> = std::fs::read_dir(&blob_dir)
.unwrap()
.map(|e| {
let p = e.unwrap().path();
(
p.file_name().unwrap().to_string_lossy().into_owned(),
std::fs::read(&p).unwrap(),
)
})
.collect();
let line: Line = "2026.07.0".parse::<LayerId>().unwrap().line().clone();
let envelope = status(1).sign(&sk, "k").unwrap();
attach_to_layout(&dest, &line, envelope.as_bytes()).unwrap();
for (name, bytes) in &before {
assert_eq!(&std::fs::read(blob_dir.join(name)).unwrap(), bytes);
}
let carried = read_from_layout(&dest, &line).unwrap().unwrap();
let parsed = LineStatus::verify_and_parse(&carried, &pk).unwrap();
assert_eq!(parsed.counter, 1);
let hex = outcome.digest.strip_prefix("sha256:").unwrap();
assert!(
blob_dir.join(hex).is_file(),
"layer manifest blob still present"
);
let envelope2 = status(2).sign(&sk, "k").unwrap();
attach_to_layout(&dest, &line, envelope2.as_bytes()).unwrap();
let index: serde_json::Value =
serde_json::from_slice(&std::fs::read(dest.join("index.json")).unwrap()).unwrap();
let count = index["manifests"]
.as_array()
.unwrap()
.iter()
.filter(|e| e["artifactType"] == LINE_STATUS_ARTIFACT_TYPE)
.count();
assert_eq!(count, 1);
}
#[test]
fn the_cache_refuses_a_counter_regression() {
let (sk, pk) = generate_root_keypair();
let tmp = tempfile::tempdir().unwrap();
let cache = StatusCache::at_root(tmp.path());
let line: Line = "2026.07.0".parse::<LayerId>().unwrap().line().clone();
let newer = status(2);
let env2 = newer.sign(&sk, "k").unwrap();
cache.update(&line, env2.as_bytes(), &newer).unwrap();
let older = status(1);
let env1 = older.sign(&sk, "k").unwrap();
let err = cache.update(&line, env1.as_bytes(), &older).unwrap_err();
assert!(matches!(
err,
LineStatusError::Stale {
presented: 1,
cached: 2,
..
}
));
let loaded = cache.load(&line, &pk).unwrap().unwrap();
assert_eq!(loaded.counter, 2);
}
#[test]
fn a_source_baseline_is_verified_and_cached_so_status_works_offline() {
use crate::source::{LayerRef, MemorySource};
let (sk, pk) = generate_root_keypair();
let tmp = tempfile::tempdir().unwrap();
let store_root = tmp.path();
let line: Line = "2026.07.0".parse::<LayerId>().unwrap().line().clone();
let doc = status(5);
let envelope = doc.sign(&sk, "k").unwrap();
let source = MemorySource::new().with_line_status(envelope.as_bytes());
let layer = LayerRef::Name("2026.07.0".parse().unwrap());
let cached = cache_baseline_from_source(&source, &layer, &line, &pk, store_root).unwrap();
assert_eq!(
cached,
Some(5),
"a carried baseline is cached at its counter"
);
let loaded = StatusCache::at_root(store_root)
.load(&line, &pk)
.unwrap()
.unwrap();
assert_eq!(loaded.counter, 5);
}
#[test]
fn a_baseline_for_the_wrong_line_is_refused_not_miscached() {
use crate::source::{LayerRef, MemorySource};
let (sk, pk) = generate_root_keypair();
let tmp = tempfile::tempdir().unwrap();
let requested: Line = "2026.07.0".parse::<LayerId>().unwrap().line().clone();
let doc = LineStatus {
line: "2026.08".into(),
counter: 5,
issued_at: "2026-08-07T00:00:00Z".into(),
support_until: None,
yanked: BTreeMap::new(),
known_problems: Vec::new(),
};
let envelope = doc.sign(&sk, "k").unwrap();
let source = MemorySource::new().with_line_status(envelope.as_bytes());
let err = cache_baseline_from_source(
&source,
&LayerRef::Name("2026.07.0".parse().unwrap()),
&requested,
&pk,
tmp.path(),
)
.unwrap_err();
assert!(
matches!(err, LineStatusError::LineMismatch { .. }),
"a baseline for the wrong line must be refused: {err}"
);
assert!(
StatusCache::at_root(tmp.path())
.load(&requested, &pk)
.unwrap()
.is_none(),
"nothing is cached under the requested line"
);
}
#[test]
fn a_source_with_no_baseline_caches_nothing_and_does_not_error() {
use crate::source::{LayerRef, MemorySource};
let (_sk, pk) = generate_root_keypair();
let tmp = tempfile::tempdir().unwrap();
let line: Line = "2026.07.0".parse::<LayerId>().unwrap().line().clone();
let source = MemorySource::new();
let cached = cache_baseline_from_source(
&source,
&LayerRef::Name("2026.07.0".parse().unwrap()),
&line,
&pk,
tmp.path(),
)
.unwrap();
assert_eq!(cached, None);
}
#[test]
fn a_baseline_signed_by_an_impostor_is_refused_not_cached() {
use crate::source::{LayerRef, MemorySource};
let (attacker_sk, _) = generate_root_keypair();
let (_real_sk, real_pk) = generate_root_keypair();
let tmp = tempfile::tempdir().unwrap();
let line: Line = "2026.07.0".parse::<LayerId>().unwrap().line().clone();
let envelope = status(5).sign(&attacker_sk, "k").unwrap();
let source = MemorySource::new().with_line_status(envelope.as_bytes());
let err = cache_baseline_from_source(
&source,
&LayerRef::Name("2026.07.0".parse().unwrap()),
&line,
&real_pk,
tmp.path(),
)
.unwrap_err();
assert!(
StatusCache::at_root(tmp.path())
.load(&line, &real_pk)
.unwrap()
.is_none(),
"a baseline that fails verification must not be cached: {err}"
);
}
#[test]
fn attaching_by_envelope_derives_the_line_from_the_document() {
use crate::deposit::{DepositSpec, DepositTool, deposit};
let (sk, pk) = generate_root_keypair();
let tmp = tempfile::tempdir().unwrap();
let dest = tmp.path().join("layout");
deposit(
&DepositSpec {
includes: Vec::new(),
layer: "2026.07.0".parse().unwrap(),
channel: "qualified".into(),
counter: 1,
issued_at: "2026-08-07T00:00:00Z".into(),
tools: vec![DepositTool {
name: "synth".into(),
version: "1".into(),
platform: None,
bytes: b"t".to_vec(),
source: None,
runner: None,
kind: None,
sdk_prefix: None,
}],
},
&sk,
"k",
&dest,
)
.unwrap();
let envelope = status(4).sign(&sk, "k").unwrap();
let (line, counter) = attach_envelope_to_layout(&dest, envelope.as_bytes()).unwrap();
assert_eq!(line.to_string(), "2026.07");
assert_eq!(counter, 4);
let carried = read_any_from_layout(&dest).unwrap().unwrap();
assert_eq!(
LineStatus::verify_and_parse(&carried, &pk).unwrap().counter,
4
);
}
#[test]
fn attaching_a_stale_document_over_a_newer_one_is_refused() {
use crate::deposit::{DepositSpec, DepositTool, deposit};
let (sk, _pk) = generate_root_keypair();
let tmp = tempfile::tempdir().unwrap();
let dest = tmp.path().join("layout");
deposit(
&DepositSpec {
includes: Vec::new(),
layer: "2026.07.0".parse().unwrap(),
channel: "qualified".into(),
counter: 1,
issued_at: "2026-08-07T00:00:00Z".into(),
tools: vec![DepositTool {
name: "synth".into(),
version: "1".into(),
platform: None,
bytes: b"t".to_vec(),
source: None,
runner: None,
kind: None,
sdk_prefix: None,
}],
},
&sk,
"k",
&dest,
)
.unwrap();
attach_envelope_to_layout(&dest, status(7).sign(&sk, "k").unwrap().as_bytes()).unwrap();
let err = attach_envelope_to_layout(&dest, status(3).sign(&sk, "k").unwrap().as_bytes())
.unwrap_err();
assert!(
matches!(
err,
LineStatusError::Stale {
presented: 3,
cached: 7,
..
}
),
"a lower counter must be refused, got {err}"
);
let msg = err.to_string();
assert!(msg.contains('3') && msg.contains('7'), "names both: {msg}");
let carried = parse_unverified(&read_any_from_layout(&dest).unwrap().unwrap()).unwrap();
assert_eq!(
carried.counter, 7,
"the newer baseline survives the attempt"
);
attach_envelope_to_layout(&dest, status(7).sign(&sk, "k").unwrap().as_bytes()).unwrap();
}
#[test]
fn an_advisory_that_could_never_fire_is_refused_at_sign_time() {
let (sk, _pk) = generate_root_keypair();
let cases: &[(&str, &str)] = &[
("2026.7.0", "not a valid YYYY.MM.P id"), ("2026.07", "missing its patch component"), ("2026.08.0", "belongs to another line"), ("2026.07.O", "letter O for zero"),
];
for (bad, why) in cases {
let mut doc = status(1);
doc.known_problems[0].affected = vec![bad.to_string()];
let err = doc.sign(&sk, "k").unwrap_err();
assert!(
matches!(err, LineStatusError::DeadReference { .. }),
"{why}: affected id {bad:?} must be refused, got: {err}"
);
let msg = err.to_string();
assert!(
msg.contains(bad) && msg.contains("2026.07") && msg.contains("re-sign"),
"the error must name the id, the line, and the fix: {msg}"
);
}
let mut doc = status(1);
doc.yanked = BTreeMap::from([("2026.8.0".to_string(), "CVE".to_string())]);
assert!(matches!(
doc.sign(&sk, "k").unwrap_err(),
LineStatusError::DeadReference { .. }
));
status(1).sign(&sk, "k").unwrap();
}
#[test]
fn attach_refuses_a_pre_signed_advisory_that_could_never_fire() {
use crate::deposit::{DepositSpec, DepositTool, deposit};
let (sk, _pk) = generate_root_keypair();
let tmp = tempfile::tempdir().unwrap();
let dest = tmp.path().join("layout");
deposit(
&DepositSpec {
includes: Vec::new(),
layer: "2026.07.0".parse().unwrap(),
channel: "qualified".into(),
counter: 1,
issued_at: "2026-08-07T00:00:00Z".into(),
tools: vec![DepositTool {
name: "synth".into(),
version: "1".into(),
platform: None,
bytes: b"t".to_vec(),
source: None,
runner: None,
kind: None,
sdk_prefix: None,
}],
},
&sk,
"k",
&dest,
)
.unwrap();
let mut doc = status(1);
doc.known_problems[0].affected = vec!["2026.7.0".to_string()];
let payload = serde_json::to_vec_pretty(&doc).unwrap();
let envelope = dsse_sign_typed(&payload, LINE_STATUS_PAYLOAD_TYPE, &sk, "k").unwrap();
let err = attach_envelope_to_layout(&dest, envelope.as_bytes()).unwrap_err();
assert!(
matches!(err, LineStatusError::DeadReference { .. }),
"got: {err}"
);
assert!(
read_any_from_layout(&dest).unwrap().is_none(),
"the dead advisory must not land in the layout"
);
}
#[test]
fn attaching_to_a_directory_that_is_not_a_layout_is_refused_before_writing() {
let (sk, _pk) = generate_root_keypair();
let tmp = tempfile::tempdir().unwrap();
let not_a_layout = tmp.path().join("somedir");
std::fs::create_dir_all(¬_a_layout).unwrap();
let envelope = status(1).sign(&sk, "k").unwrap();
let err = attach_envelope_to_layout(¬_a_layout, envelope.as_bytes()).unwrap_err();
assert!(
matches!(err, LineStatusError::NotALayout { .. }),
"got: {err}"
);
assert!(
err.to_string().contains("varve deposit"),
"the error must carry its fix: {err}"
);
assert!(
!not_a_layout.join("blobs").exists(),
"nothing may be written into a directory that is not a layout"
);
}
#[test]
fn the_unsigned_document_mistake_is_named_not_wrapped() {
let raw = serde_json::to_string_pretty(&status(1)).unwrap();
let err = not_an_envelope(&raw);
let msg = err.to_string();
assert!(
msg.contains("UNSIGNED") && msg.contains("varve sign-status"),
"raw document must be diagnosed with its fix: {msg}"
);
let msg = not_an_envelope("garbage").to_string();
assert!(
msg.contains("not a DSSE envelope") && msg.contains("varve sign-status"),
"got: {msg}"
);
let (_sk, pk) = generate_root_keypair();
let err = LineStatus::verify_and_parse(raw.as_bytes(), &pk).unwrap_err();
assert!(err.to_string().contains("UNSIGNED"), "got: {err}");
}
#[test]
fn a_deposit_layouts_baseline_is_readable_without_naming_the_line() {
use crate::deposit::{DepositSpec, DepositTool, deposit};
let (sk, pk) = generate_root_keypair();
let tmp = tempfile::tempdir().unwrap();
let dest = tmp.path().join("layout");
let spec = DepositSpec {
includes: Vec::new(),
layer: "2026.07.0".parse().unwrap(),
channel: "qualified".into(),
counter: 1,
issued_at: "2026-08-07T00:00:00Z".into(),
tools: vec![DepositTool {
name: "synth".into(),
version: "1".into(),
platform: None,
bytes: b"t".to_vec(),
source: None,
runner: None,
kind: None,
sdk_prefix: None,
}],
};
deposit(&spec, &sk, "k", &dest).unwrap();
assert!(read_any_from_layout(&dest).unwrap().is_none());
let line: Line = "2026.07.0".parse::<LayerId>().unwrap().line().clone();
let envelope = status(3).sign(&sk, "k").unwrap();
attach_to_layout(&dest, &line, envelope.as_bytes()).unwrap();
let carried = read_any_from_layout(&dest).unwrap().unwrap();
let parsed = LineStatus::verify_and_parse(&carried, &pk).unwrap();
assert_eq!(parsed.counter, 3);
}
}