use anyhow::Context;
use varve_core::Store;
struct DocsRow {
format: String,
entry: Option<String>,
title: Option<String>,
documents: Option<String>,
}
struct Row {
name: String,
version: Option<String>,
kind: String,
known_kind: bool,
platform: String,
digest: String,
dispatch: &'static str,
docs: Option<DocsRow>,
ingest_proof: String,
proof_signer: Option<String>,
present: bool,
layer: String,
realm: String,
}
const DISPATCHED: &str = "dispatched";
const HELD: &str = "held";
const UNKNOWN: &str = "unknown";
pub fn run(store: &Store, layer: Option<&str>, json: bool) -> anyhow::Result<()> {
let target = crate::export_target(store, layer)?;
let layers = crate::composition_for_export(&target)?;
let host = varve_core::host_platform();
let mut rows = Vec::new();
for l in &layers {
let payload = std::fs::read(l.entry.root.join("layer.json")).with_context(|| {
format!(
"cannot read the signed manifest of layer {} — the store entry is incomplete",
l.entry.layer
)
})?;
let manifest = varve_core::LayerManifest::parse(&payload)?;
for e in &manifest.entries {
let parsed = e.kind();
let kind = match &parsed {
Ok(k) => k.as_str().to_string(),
Err(varve_core::UnknownKind(raw)) => raw.clone(),
};
if parsed == Ok(varve_core::PayloadKind::Layer) {
continue;
}
let name = e
.annotations
.get("eu.pulseengine.tool")
.cloned()
.unwrap_or_else(|| "(unnamed entry)".to_string());
rows.push(Row {
version: e.annotations.get("eu.pulseengine.tool.version").cloned(),
kind,
known_kind: parsed.is_ok(),
platform: e
.annotations
.get(varve_core::platform::ANN_PLATFORM)
.cloned()
.unwrap_or_else(|| "any".to_string()),
digest: e.digest.clone(),
dispatch: match parsed {
Ok(k) if k.is_dispatchable() => DISPATCHED,
Ok(_) => HELD,
Err(_) => UNKNOWN,
},
ingest_proof: match e.ingest_proof() {
Ok(p) => varve_core::IngestProof::label(p).to_string(),
Err(varve_core::UnknownProof(raw)) => raw,
},
proof_signer: e.annotations.get(varve_core::ANN_PROOF_SIGNER).cloned(),
docs: e
.annotations
.get(varve_core::deposit::ANN_DOCS_FORMAT)
.map(|format| DocsRow {
format: format.clone(),
entry: e
.annotations
.get(varve_core::deposit::ANN_DOCS_ENTRY)
.cloned(),
title: e
.annotations
.get(varve_core::deposit::ANN_DOCS_TITLE)
.cloned(),
documents: e
.annotations
.get(varve_core::deposit::ANN_DOCS_DOCUMENTS)
.cloned(),
}),
present: store_of(l).entry_path(&l.entry, e).is_some(),
layer: l.entry.layer.to_string(),
realm: l.realm.clone(),
name,
});
}
}
rows.sort_by(|a, b| {
(&a.layer, &a.kind, &a.name, &a.version, &a.platform).cmp(&(
&b.layer,
&b.kind,
&b.name,
&b.version,
&b.platform,
))
});
if json {
print_json(&target, &layers, &rows, &host);
} else {
print_text(&target, &layers, &rows, &host);
}
Ok(())
}
fn store_of(l: &crate::ComposedLayer) -> &Store {
&l.store
}
fn print_json(
target: &crate::ExportTarget,
layers: &[crate::ComposedLayer],
rows: &[Row],
host: &str,
) {
let composition: Vec<_> = layers
.iter()
.map(|l| {
serde_json::json!({
"layer": l.entry.layer.to_string(),
"manifest_digest": l.entry.digest,
"realm": l.realm,
"root": l.entry.digest == target.entry.digest,
})
})
.collect();
let payloads: Vec<_> = rows
.iter()
.map(|r| {
serde_json::json!({
"name": r.name,
"version": r.version,
"kind": r.kind,
"known_kind": r.known_kind,
"platform": r.platform,
"dispatch": r.dispatch,
"ingest_proof": r.ingest_proof,
"proof_signer": r.proof_signer,
"digest": r.digest,
"present": r.present,
"layer": r.layer,
"realm": r.realm,
"docs_format": r.docs.as_ref().map(|d| &d.format),
"docs_entry": r.docs.as_ref().and_then(|d| d.entry.as_ref()),
"docs_title": r.docs.as_ref().and_then(|d| d.title.as_ref()),
"docs_documents": r.docs.as_ref().and_then(|d| d.documents.as_ref()),
})
})
.collect();
let doc = serde_json::json!({
"command": "inspect",
"layer": target.entry.layer.to_string(),
"channel": target.entry.channel,
"realm": layers
.iter()
.find(|l| l.entry.digest == target.entry.digest)
.map(|l| l.realm.clone()),
"manifest_digest": target.entry.digest,
"host_platform": host,
"composition": composition,
"payloads": payloads,
"summary": {
"payloads": rows.len(),
"dispatched": rows.iter().filter(|r| r.dispatch == DISPATCHED).count(),
"held": rows.iter().filter(|r| r.dispatch == HELD).count(),
"layers": layers.len(),
"unverified": rows.iter().filter(|r| r.ingest_proof == "unverified").count(),
"unrecorded": rows.iter().filter(|r| r.ingest_proof == "unrecorded").count(),
},
});
println!(
"{}",
serde_json::to_string_pretty(&doc).expect("the inspect report serialises")
);
}
fn print_text(
target: &crate::ExportTarget,
layers: &[crate::ComposedLayer],
rows: &[Row],
host: &str,
) {
let realm = layers
.iter()
.find(|l| l.entry.digest == target.entry.digest)
.map(|l| l.realm.as_str());
match realm {
Some(r) => println!(
"layer {} ({}) realm '{}' {}",
target.entry.layer, target.entry.channel, r, target.entry.digest
),
None => println!(
"layer {} ({}) {}",
target.entry.layer, target.entry.channel, target.entry.digest
),
}
if layers.len() > 1 {
println!("composition: {} layers —", layers.len());
for l in layers {
println!(
" {} {} (verified against realm '{}'){}",
l.entry.layer,
l.entry.digest,
l.realm,
if l.entry.digest == target.entry.digest {
" [pinned]"
} else {
""
}
);
}
}
if rows.is_empty() {
println!("\nno payloads — this layer carries no entries beyond its composition");
return;
}
let dispatched = rows.iter().filter(|r| r.dispatch == DISPATCHED).count();
let held = rows.len() - dispatched;
println!(
"\n{} payload(s): {dispatched} DISPATCHED, {held} HELD (platform {host})\n",
rows.len()
);
let w = |f: fn(&Row) -> &str, head: &str| {
rows.iter()
.map(|r| f(r).chars().count())
.chain(std::iter::once(head.chars().count()))
.max()
.unwrap_or(1)
};
let (wn, wk, wp) = (w(|r| &r.name, "NAME"), w(|r| &r.kind, "KIND"), {
rows.iter()
.map(|r| r.platform.chars().count())
.chain(std::iter::once(8))
.max()
.unwrap_or(8)
});
let wv = rows
.iter()
.map(|r| r.version.as_deref().unwrap_or("-").chars().count())
.chain(std::iter::once(7))
.max()
.unwrap_or(7);
println!(
" {:<12}{:<wk$} {:<wn$} {:<wv$} {:<wp$} LAYER",
"", "KIND", "NAME", "VERSION", "PLATFORM"
);
for r in rows {
println!(
" {:<12}{:<wk$} {:<wn$} {:<wv$} {:<wp$} {}{}",
r.dispatch.to_uppercase(),
r.kind,
r.name,
r.version.as_deref().unwrap_or("-"),
r.platform,
r.layer,
if r.present {
""
} else {
" (not laid down here)"
},
);
}
if held > 0 {
println!(
"\nHELD payloads are stored and verified but NOT on your PATH — `varve which` will \
not find them, by design. Only a `tool` is dispatched by name. See \
`varve docs inspect`."
);
}
let docs: Vec<(&Row, &DocsRow)> = rows
.iter()
.filter_map(|r| r.docs.as_ref().map(|d| (r, d)))
.collect();
if !docs.is_empty() {
println!("\ndocumentation for the versions this layer pins:");
for (row, d) in &docs {
let label = d.title.as_deref().unwrap_or(&row.name);
println!(
" {label} {} ({})",
row.version.as_deref().unwrap_or("-"),
d.format
);
if let Some(entry) = &d.entry {
println!(" starts at {entry}");
}
if let Some(of) = &d.documents {
println!(" documents {of} (varve export-docs --for {of})");
}
}
match docs.as_slice() {
[(row, _)] => {
println!("\n read it, copying nothing: varve-serve");
println!(
" or write a copy: varve export-docs --out ./doc ({})",
row.name
);
}
many => {
println!(
"\n {} documents, so name one: varve-serve --select <NAME>",
many.len()
);
println!(
" or: varve export-docs --out ./doc --select <NAME>"
);
}
}
}
if rows.iter().any(|r| !r.known_kind) {
println!(
"\nAn entry above carries a payload kind this varve does not know. Its bytes still \
verify against the signed digest — only the adapters that must DO something \
kind-specific will refuse it. A newer varve may handle it."
);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn row(name: &str, docs: Option<DocsRow>) -> Row {
Row {
name: name.into(),
version: Some("1.0.0".into()),
kind: if docs.is_some() {
"docs".into()
} else {
"tool".into()
},
known_kind: true,
platform: "any".into(),
digest: "sha256:0".into(),
dispatch: if docs.is_some() { HELD } else { DISPATCHED },
docs,
ingest_proof: "cosign-sums".into(),
proof_signer: None,
present: true,
layer: "2026.09.9".into(),
realm: "t".into(),
}
}
#[test]
fn both_renderings_report_the_same_documents() {
let rows = [
row("rivet", None),
row(
"handbook",
Some(DocsRow {
format: "pdf".into(),
entry: None,
title: Some("The handbook".into()),
documents: None,
}),
),
];
let in_text: Vec<&str> = rows
.iter()
.filter(|r| r.docs.is_some())
.map(|r| r.name.as_str())
.collect();
let in_json: Vec<&str> = rows
.iter()
.filter(|r| r.docs.as_ref().map(|d| !d.format.is_empty()) == Some(true))
.map(|r| r.name.as_str())
.collect();
assert_eq!(in_text, in_json, "the two renderings disagree");
assert_eq!(in_text, vec!["handbook"]);
}
#[test]
fn a_document_is_never_reported_as_dispatched() {
let d = row(
"handbook",
Some(DocsRow {
format: "html".into(),
entry: Some("index.html".into()),
title: None,
documents: None,
}),
);
assert_eq!(d.dispatch, HELD);
assert!(!varve_core::PayloadKind::Docs.is_dispatchable());
}
}