1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
//! Sources — where bytes come from. Pluggable by design; trusted by nobody
//! (DD-003).
//!
//! A source can *obtain* bytes: a manifest by layer name or digest, a blob by
//! digest. It has no voice in whether those bytes are *accepted* — signature
//! and digest verification run against the trust root after every fetch, so
//! swapping the source can change availability, never a verdict. The install
//! pipeline (`crate::install`) enforces this by construction: nothing a
//! `LayerSource` returns reaches the core without passing the same checks.
use crate::layer::LayerId;
/// UNTRUSTED discovery: does `bytes` look like a manifest for `id`, either
/// raw or wrapped in a DSSE envelope? Sources use this to answer name/digest
/// lookups; it grants nothing — the install pipeline re-verifies signature
/// and digest on whatever a source returns.
fn discovery_matches(bytes: &[u8], layer: &LayerRef) -> bool {
use crate::manifest::LayerManifest;
let candidate_payloads = || -> Vec<Vec<u8>> {
let mut out = vec![bytes.to_vec()];
if let Ok(text) = std::str::from_utf8(bytes)
&& let Ok(env) = wsc::dsse::DsseEnvelope::from_json(text)
&& let Ok(payload) = env.payload_bytes()
{
out.push(payload);
}
out
};
match layer {
LayerRef::Digest(digest) => candidate_payloads()
.iter()
.any(|p| &crate::store::manifest_digest(p) == digest),
LayerRef::Name(id) => candidate_payloads()
.iter()
.any(|p| LayerManifest::parse(p).is_ok_and(|m| &m.layer == id)),
}
}
/// Reference to a layer a source should produce the manifest for.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LayerRef {
/// By name — discovery; the returned manifest's own annotations and
/// digest are then checked against the pin.
Name(LayerId),
/// By exact manifest digest (`sha256:<hex>`).
Digest(String),
}
/// Failures a source may report. `NotFound` is honest absence; everything
/// else is transport trouble. There is deliberately no way for a source to
/// report "trust me" — trust is not its department.
#[derive(Debug, thiserror::Error)]
pub enum SourceError {
#[error("source has no layer matching {0}")]
NotFound(String),
/// Absence with a KNOWN cause: the source is an archive of one platform and
/// the caller wants another. Distinct from `NotFound` because the operator's
/// next move is different — nothing is corrupt, nothing is missing from the
/// archive that belongs in it, and no amount of re-copying the media will
/// help (varve#80).
#[error(
"this archive carries no payload for {wanted} — it was archived for {archived_for}, and \
`varve archive` exports only the payloads the archiving machine installed, so it holds \
{archived_for} payloads and nothing else (blob {digest} is not in it). Install the layer \
on a machine running {wanted} and archive it there to carry {wanted} across the gap."
)]
NoPayloadForPlatform {
digest: String,
wanted: String,
archived_for: String,
},
#[error("source transport error: {0}")]
Transport(String),
}
/// Where bytes come from. Implementations ship in varve (public registry,
/// archived core, test doubles); the trait is the seam an entitlement
/// plug-in would use — and the reason none of them can influence acceptance.
pub trait LayerSource {
/// Fetch the manifest bytes for a layer reference.
fn fetch_manifest(&self, layer: &LayerRef) -> Result<Vec<u8>, SourceError>;
/// Fetch a blob (a tool binary) by its digest (`sha256:<hex>`).
fn fetch_blob(&self, digest: &str) -> Result<Vec<u8>, SourceError>;
/// Fetch the baseline line-status DSSE envelope this source carries
/// beside the layer, if any (REQ-STATUS-DIST-001). Returns the opaque
/// envelope bytes — the source is *not* trusted to have verified them;
/// the caller re-verifies against the trust root before caching. A
/// source that carries no baseline returns `Ok(None)`, which is not an
/// error: line-status is updatable evidence, absent on some layers.
fn fetch_line_status(&self, _layer: &LayerRef) -> Result<Option<Vec<u8>>, SourceError> {
Ok(None)
}
/// Fetch the realm's signed line-index envelope for this line, if the
/// source carries one (REQ-INDEXAUTH-001). Same contract as
/// `fetch_line_status`: opaque bytes, re-verified by the caller against
/// the trust root. The source is never trusted to have checked it — it is
/// precisely the party this document exists to constrain.
fn fetch_line_index(&self, _line: &str) -> Result<Option<Vec<u8>>, SourceError> {
Ok(None)
}
/// Fetch the attestations this source carries beside the layer as
/// referrer artifacts (REQ-ATTEST-002). Same contract as
/// `fetch_line_status`: OPAQUE, UNTRUSTED bytes. The source is never
/// trusted to have verified a statement — it is the party that would
/// benefit from a forged one — so the caller persists them verbatim and
/// `varve verify` re-checks each against the trust root.
///
/// A source carrying none returns `Ok(vec![])`, which is not an error: most
/// layers carry no third-party evidence, and demanding some would make
/// varve's availability depend on other people's publishing habits.
fn fetch_attestations(
&self,
_layer: &LayerRef,
) -> Result<Vec<crate::attestcarry::CarriedAttestation>, SourceError> {
Ok(Vec::new())
}
/// The layer ids this source is willing to serve for a line. Used to
/// detect OMISSION against the signed index. A source that cannot
/// enumerate returns `Ok(None)` — distinct from `Ok(Some(vec![]))`, which
/// means "I enumerate, and I have nothing", and would flag every indexed
/// layer as hidden.
fn served_layers(&self, _line: &str) -> Result<Option<Vec<String>>, SourceError> {
Ok(None)
}
}
/// In-memory source — the test double, and the reference for how little a
/// source is trusted to do.
#[derive(Debug, Default)]
pub struct MemorySource {
manifests: Vec<Vec<u8>>,
blobs: std::collections::BTreeMap<String, Vec<u8>>,
line_status: Option<Vec<u8>>,
line_index: Option<Vec<u8>>,
served: Option<Vec<String>>,
attestations: Vec<crate::attestcarry::CarriedAttestation>,
}
impl MemorySource {
pub fn new() -> Self {
Self::default()
}
pub fn with_manifest(mut self, bytes: &[u8]) -> Self {
self.manifests.push(bytes.to_vec());
self
}
pub fn with_blob(mut self, digest: &str, bytes: &[u8]) -> Self {
self.blobs.insert(digest.to_string(), bytes.to_vec());
self
}
/// Attach a baseline line-status envelope the source carries beside the
/// layer (REQ-STATUS-DIST-001).
/// Carry a signed line index (REQ-INDEXAUTH-001).
pub fn with_line_index(mut self, envelope: &[u8]) -> Self {
self.line_index = Some(envelope.to_vec());
self
}
/// What this source admits to serving. Setting it makes the source
/// enumerable, which is what lets omission be detected — a source that
/// never sets it cannot be accused of hiding.
pub fn serving(mut self, layers: &[&str]) -> Self {
self.served = Some(layers.iter().map(|s| s.to_string()).collect());
self
}
pub fn with_line_status(mut self, envelope: &[u8]) -> Self {
self.line_status = Some(envelope.to_vec());
self
}
/// Carry an attestation beside the layer (REQ-ATTEST-002). The statement's
/// digest is derived from the bytes handed over, not declared: a source
/// that could name its own content addresses would be trusted about
/// something, and it is trusted about nothing.
pub fn with_attestation(mut self, statement: &[u8], attested_bytes: &[u8]) -> Self {
self.attestations
.push(crate::attestcarry::CarriedAttestation {
statement_digest: crate::store::manifest_digest(statement),
statement: statement.to_vec(),
bytes: attested_bytes.to_vec(),
});
self
}
}
/// Directory-shaped source: `<root>/manifests/sha256-<hex>` and
/// `<root>/blobs/sha256-<hex>`. The reading half of the archived core —
/// and, in tests, the second transport for the two-sources-same-verdict
/// kill-criterion.
#[derive(Debug)]
pub struct DirSource {
root: std::path::PathBuf,
}
impl DirSource {
pub fn at(root: impl Into<std::path::PathBuf>) -> Self {
DirSource { root: root.into() }
}
/// Write a manifest + blobs into the directory layout (the producing
/// side, used by tests and by `archive` later).
pub fn put(&self, manifest_bytes: &[u8], blobs: &[(&str, &[u8])]) -> std::io::Result<()> {
let manifests = self.root.join("manifests");
let blob_dir = self.root.join("blobs");
std::fs::create_dir_all(&manifests)?;
std::fs::create_dir_all(&blob_dir)?;
let digest = crate::store::manifest_digest(manifest_bytes);
std::fs::write(manifests.join(digest.replace(':', "-")), manifest_bytes)?;
for (digest, bytes) in blobs {
std::fs::write(blob_dir.join(digest.replace(':', "-")), bytes)?;
}
Ok(())
}
}
impl LayerSource for DirSource {
fn fetch_manifest(&self, layer: &LayerRef) -> Result<Vec<u8>, SourceError> {
let dir = self.root.join("manifests");
let entries = std::fs::read_dir(&dir)
.map_err(|e| SourceError::Transport(format!("{}: {e}", dir.display())))?;
for entry in entries.filter_map(|e| e.ok()) {
let bytes =
std::fs::read(entry.path()).map_err(|e| SourceError::Transport(e.to_string()))?;
if discovery_matches(&bytes, layer) {
return Ok(bytes);
}
}
Err(SourceError::NotFound(format!("{layer:?}")))
}
fn fetch_blob(&self, digest: &str) -> Result<Vec<u8>, SourceError> {
let path = self.root.join("blobs").join(digest.replace(':', "-"));
match std::fs::read(&path) {
Ok(bytes) => Ok(bytes),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
Err(SourceError::NotFound(digest.to_string()))
}
Err(e) => Err(SourceError::Transport(e.to_string())),
}
}
// `fetch_line_index` and `served_layers` stay at the trait defaults, and
// both defaults are the truthful answer rather than a stub
// (REQ-INDEXAUTH-001). This layout is `manifests/` + `blobs/` addressed by
// digest: it has nowhere to carry a per-line document, and its manifest
// directory is whatever someone copied there — not a listing of the line.
// `Ok(None)` for `served_layers` therefore means "cannot enumerate", which
// is exactly right; returning `Ok(Some(...))` of the files present would
// accuse an honest air-gapped copy of hiding every layer it was not given.
// A realm that declares `signed-index = true` consequently cannot be
// installed from a bare DirSource at all — it fails closed, naming the
// realm, which is the correct outcome for a transport that cannot carry
// the evidence the realm promised. Use an oci-layout archive instead.
}
impl LayerSource for MemorySource {
fn fetch_manifest(&self, layer: &LayerRef) -> Result<Vec<u8>, SourceError> {
self.manifests
.iter()
.find(|bytes| discovery_matches(bytes, layer))
.cloned()
.ok_or_else(|| SourceError::NotFound(format!("{layer:?}")))
}
fn fetch_blob(&self, digest: &str) -> Result<Vec<u8>, SourceError> {
self.blobs
.get(digest)
.cloned()
.ok_or_else(|| SourceError::NotFound(digest.to_string()))
}
fn fetch_line_index(&self, _line: &str) -> Result<Option<Vec<u8>>, SourceError> {
Ok(self.line_index.clone())
}
fn served_layers(&self, _line: &str) -> Result<Option<Vec<String>>, SourceError> {
Ok(self.served.clone())
}
fn fetch_line_status(&self, _layer: &LayerRef) -> Result<Option<Vec<u8>>, SourceError> {
Ok(self.line_status.clone())
}
fn fetch_attestations(
&self,
_layer: &LayerRef,
) -> Result<Vec<crate::attestcarry::CarriedAttestation>, SourceError> {
Ok(self.attestations.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
// rivet: verifies REQ-STATUS-DIST-001
#[test]
fn a_source_carrying_a_baseline_line_status_yields_it() {
let envelope = b"an-opaque-dsse-envelope";
let source = MemorySource::new().with_line_status(envelope);
let got = source
.fetch_line_status(&LayerRef::Name("2026.07.0".parse().unwrap()))
.unwrap();
assert_eq!(
got.as_deref(),
Some(envelope.as_slice()),
"a source that carries a baseline line-status must hand it back for caching"
);
}
// rivet: verifies REQ-ATTEST-002
#[test]
fn a_source_carrying_attestations_hands_over_both_blobs_and_one_without_is_not_an_error() {
let source = MemorySource::new().with_attestation(b"a-statement-envelope", b"the-evidence");
let got = source
.fetch_attestations(&LayerRef::Name("2026.07.0".parse().unwrap()))
.unwrap();
assert_eq!(got.len(), 1);
assert_eq!(
got[0].bytes, b"the-evidence",
"the attested bytes must travel beside the statement — a claim with nothing to \
check it against is what crossing the air gap must never produce"
);
assert_eq!(
got[0].statement_digest,
crate::store::manifest_digest(b"a-statement-envelope"),
"the digest is derived from the bytes; a source never declares its own address"
);
// Absence is emptiness, not failure: most layers carry no third-party
// evidence, and requiring some would make availability depend on other
// people's publishing habits.
assert!(
MemorySource::new()
.fetch_attestations(&LayerRef::Name("2026.07.0".parse().unwrap()))
.unwrap()
.is_empty()
);
}
// rivet: verifies REQ-STATUS-DIST-001
#[test]
fn a_source_without_a_line_status_is_not_an_error() {
let source = MemorySource::new();
let got = source
.fetch_line_status(&LayerRef::Name("2026.07.0".parse().unwrap()))
.unwrap();
assert_eq!(
got, None,
"an absent line-status is Ok(None), never an error"
);
}
}