Skip to main content

varve_core/
mirror.rs

1//! A realm served from more than one place (REQ-MIRROR-001).
2//!
3//! Today a realm names exactly one registry. If it is unreachable — an outage,
4//! a partition, an org-level package change, a blocked region — no consumer can
5//! install or update, and a fresh one cannot bootstrap at all.
6//!
7//! ## Why mirroring is safe here, and why that is the point
8//!
9//! In most package managers a mirror is a trust decision: you are choosing
10//! another party to believe. Here it is not. A layer is accepted because its
11//! manifest verifies against the realm's trust root and its payload digests
12//! match — the registry is transport, not authority, and `source.rs` says so
13//! in its first paragraph: *"A source can obtain bytes. It has no voice in
14//! whether those bytes are accepted."*
15//!
16//! So a tampered mirror fails the DSSE check and a truncated one fails the
17//! digest check, exactly as the primary would. A second source widens
18//! availability and not the trust surface, which is precisely why it is worth
19//! having.
20//!
21//! ## The clause that matters
22//!
23//! Falling through on failure is the whole feature, and it is also the way to
24//! ruin it. If "the bytes did not verify" were a reason to try the next
25//! source, then a mirror serving bad bytes would be silently skipped — and the
26//! single most interesting event this design can surface, an attacker or a
27//! corruption at one source, would become invisible. The system would look
28//! healthier the more it was attacked.
29//!
30//! That is structural here rather than a rule to remember: verification runs
31//! ABOVE this type, in the install pipeline, on whatever a source returns.
32//! This type only sees `SourceError`, which cannot express "did not verify" —
33//! there is deliberately no such variant. A future refactor that tried to make
34//! one would have to change `source.rs`'s contract to do it.
35
36use crate::source::{LayerRef, LayerSource, SourceError};
37use std::cell::RefCell;
38
39/// One named place a realm's layers can be obtained from.
40pub struct Mirror {
41    /// How the source is named to an operator — a registry URL, a path.
42    pub label: String,
43    pub source: Box<dyn LayerSource>,
44}
45
46/// An ordered list of sources for one realm.
47///
48/// Order is the realm's stated preference, not a race: a deterministic order
49/// means an operator can predict which source served them, and a run that
50/// picked a different mirror each time would make an incident unreproducible.
51pub struct Mirrors {
52    mirrors: Vec<Mirror>,
53    /// The label of the source that last answered, for reporting.
54    served_by: RefCell<Option<String>>,
55    /// Why each earlier source did not answer, for the error if none does.
56    attempts: RefCell<Vec<(String, String)>>,
57}
58
59impl Mirrors {
60    pub fn new(mirrors: Vec<Mirror>) -> Self {
61        Mirrors {
62            mirrors,
63            served_by: RefCell::new(None),
64            attempts: RefCell::new(Vec::new()),
65        }
66    }
67
68    /// Which source last answered (REQ-MIRROR-001 clause 4).
69    ///
70    /// Reportable *before* an incident, not only during one: an operator who
71    /// cannot tell that they have been on a mirror for a month cannot tell
72    /// that the primary has been down for a month.
73    pub fn served_by(&self) -> Option<String> {
74        self.served_by.borrow().clone()
75    }
76
77    /// What was tried, and what each said.
78    pub fn attempts(&self) -> Vec<(String, String)> {
79        self.attempts.borrow().clone()
80    }
81
82    pub fn labels(&self) -> Vec<&str> {
83        self.mirrors.iter().map(|m| m.label.as_str()).collect()
84    }
85
86    /// Try each source in order.
87    ///
88    /// `NotFound` and `Transport` both continue: a mirror that has not synced
89    /// this layer yet is as unhelpful as one that is unreachable, and neither
90    /// says anything about the bytes. Nothing else can arrive here —
91    /// `SourceError` has no variant meaning "did not verify", by design.
92    fn try_each<T>(
93        &self,
94        what: &str,
95        mut f: impl FnMut(&dyn LayerSource) -> Result<T, SourceError>,
96    ) -> Result<T, SourceError> {
97        self.attempts.borrow_mut().clear();
98        if self.mirrors.is_empty() {
99            return Err(SourceError::Transport(
100                "this realm declares no sources at all".into(),
101            ));
102        }
103        for m in &self.mirrors {
104            match f(m.source.as_ref()) {
105                Ok(v) => {
106                    *self.served_by.borrow_mut() = Some(m.label.clone());
107                    return Ok(v);
108                }
109                Err(e) => {
110                    self.attempts
111                        .borrow_mut()
112                        .push((m.label.clone(), e.to_string()));
113                }
114            }
115        }
116        let tried = self
117            .attempts
118            .borrow()
119            .iter()
120            .map(|(l, e)| format!("\n  {l}: {e}"))
121            .collect::<String>();
122        Err(SourceError::Transport(format!(
123            "no source could supply {what}. Tried {} source(s):{tried}",
124            self.mirrors.len()
125        )))
126    }
127}
128
129impl LayerSource for Mirrors {
130    fn fetch_manifest(&self, layer: &LayerRef) -> Result<Vec<u8>, SourceError> {
131        self.try_each("the layer manifest", |s| s.fetch_manifest(layer))
132    }
133
134    fn fetch_blob(&self, digest: &str) -> Result<Vec<u8>, SourceError> {
135        self.try_each(&format!("blob {digest}"), |s| s.fetch_blob(digest))
136    }
137
138    fn fetch_line_status(&self, layer: &LayerRef) -> Result<Option<Vec<u8>>, SourceError> {
139        // `Ok(None)` is "this source carries none", which is not an error and
140        // not a reason to keep looking on THIS method — but a mirror that has
141        // the status when the primary does not is worth reaching, so an
142        // explicit None continues while an error also continues.
143        self.try_each_optional("the line-status document", |s| s.fetch_line_status(layer))
144    }
145
146    fn fetch_line_index(&self, line: &str) -> Result<Option<Vec<u8>>, SourceError> {
147        self.try_each_optional("the line index", |s| s.fetch_line_index(line))
148    }
149
150    fn fetch_attestations(
151        &self,
152        layer: &LayerRef,
153    ) -> Result<Vec<crate::attestcarry::CarriedAttestation>, SourceError> {
154        self.try_each("the attestations", |s| s.fetch_attestations(layer))
155    }
156}
157
158impl Mirrors {
159    /// For the `Option`-returning fetches: a source that returns `None` has
160    /// answered honestly, but another source may still carry the document, so
161    /// keep looking and report `None` only when nobody has it.
162    fn try_each_optional(
163        &self,
164        what: &str,
165        mut f: impl FnMut(&dyn LayerSource) -> Result<Option<Vec<u8>>, SourceError>,
166    ) -> Result<Option<Vec<u8>>, SourceError> {
167        self.attempts.borrow_mut().clear();
168        let mut any_answered = false;
169        for m in &self.mirrors {
170            match f(m.source.as_ref()) {
171                Ok(Some(v)) => {
172                    *self.served_by.borrow_mut() = Some(m.label.clone());
173                    return Ok(Some(v));
174                }
175                Ok(None) => {
176                    any_answered = true;
177                    self.attempts
178                        .borrow_mut()
179                        .push((m.label.clone(), "carries none".into()));
180                }
181                Err(e) => {
182                    self.attempts
183                        .borrow_mut()
184                        .push((m.label.clone(), e.to_string()));
185                }
186            }
187        }
188        if any_answered {
189            // At least one source answered "I do not carry this", which is a
190            // legitimate absence rather than a failure to reach anyone.
191            return Ok(None);
192        }
193        let tried = self
194            .attempts
195            .borrow()
196            .iter()
197            .map(|(l, e)| format!("\n  {l}: {e}"))
198            .collect::<String>();
199        Err(SourceError::Transport(format!(
200            "no source could be reached for {what}:{tried}"
201        )))
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use super::*;
208    use std::cell::Cell;
209    use std::rc::Rc;
210
211    struct Fake {
212        manifest: Option<Vec<u8>>,
213        err: Option<SourceError>,
214        status: Option<Option<Vec<u8>>>,
215        calls: Rc<Cell<usize>>,
216    }
217
218    fn ok(bytes: &[u8]) -> Box<Fake> {
219        Box::new(Fake {
220            manifest: Some(bytes.to_vec()),
221            err: None,
222            status: Some(Some(bytes.to_vec())),
223            calls: Rc::new(Cell::new(0)),
224        })
225    }
226    fn down(msg: &str) -> Box<Fake> {
227        Box::new(Fake {
228            manifest: None,
229            err: Some(SourceError::Transport(msg.into())),
230            status: None,
231            calls: Rc::new(Cell::new(0)),
232        })
233    }
234    fn empty() -> Box<Fake> {
235        Box::new(Fake {
236            manifest: None,
237            err: Some(SourceError::NotFound("layer".into())),
238            status: Some(None),
239            calls: Rc::new(Cell::new(0)),
240        })
241    }
242
243    impl LayerSource for Fake {
244        fn fetch_manifest(&self, _l: &LayerRef) -> Result<Vec<u8>, SourceError> {
245            self.calls.set(self.calls.get() + 1);
246            match (&self.manifest, &self.err) {
247                (Some(m), _) => Ok(m.clone()),
248                (None, Some(e)) => Err(clone_err(e)),
249                _ => Err(SourceError::NotFound("x".into())),
250            }
251        }
252        fn fetch_blob(&self, _d: &str) -> Result<Vec<u8>, SourceError> {
253            self.fetch_manifest(&LayerRef::Digest("x".into()))
254        }
255        fn fetch_line_status(&self, _l: &LayerRef) -> Result<Option<Vec<u8>>, SourceError> {
256            self.calls.set(self.calls.get() + 1);
257            match (&self.status, &self.err) {
258                (Some(s), _) => Ok(s.clone()),
259                (None, Some(e)) => Err(clone_err(e)),
260                _ => Ok(None),
261            }
262        }
263        fn fetch_line_index(&self, _line: &str) -> Result<Option<Vec<u8>>, SourceError> {
264            self.fetch_line_status(&LayerRef::Digest("x".into()))
265        }
266        fn fetch_attestations(
267            &self,
268            _l: &LayerRef,
269        ) -> Result<Vec<crate::attestcarry::CarriedAttestation>, SourceError> {
270            self.calls.set(self.calls.get() + 1);
271            match (&self.manifest, &self.err) {
272                (Some(_), _) => Ok(Vec::new()),
273                (None, Some(e)) => Err(clone_err(e)),
274                _ => Ok(Vec::new()),
275            }
276        }
277    }
278
279    fn clone_err(e: &SourceError) -> SourceError {
280        match e {
281            SourceError::NotFound(s) => SourceError::NotFound(s.clone()),
282            SourceError::Transport(s) => SourceError::Transport(s.clone()),
283            other => SourceError::Transport(other.to_string()),
284        }
285    }
286
287    fn mirrors(v: Vec<(&str, Box<Fake>)>) -> Mirrors {
288        Mirrors::new(
289            v.into_iter()
290                .map(|(l, s)| Mirror {
291                    label: l.into(),
292                    source: s as Box<dyn LayerSource>,
293                })
294                .collect(),
295        )
296    }
297
298    // rivet: verifies REQ-MIRROR-001
299    #[test]
300    fn the_first_source_that_answers_serves_the_layer() {
301        let m = mirrors(vec![("primary", ok(b"manifest")), ("mirror", ok(b"other"))]);
302        assert_eq!(
303            m.fetch_manifest(&LayerRef::Digest("d".into())).unwrap(),
304            b"manifest".to_vec()
305        );
306        assert_eq!(m.served_by().as_deref(), Some("primary"));
307    }
308
309    /// The whole point: an unreachable primary must not stop an install.
310    // rivet: verifies REQ-MIRROR-001
311    #[test]
312    fn an_unreachable_source_falls_through_to_the_next() {
313        let m = mirrors(vec![
314            ("primary", down("dial tcp: no such host")),
315            ("mirror", ok(b"manifest")),
316        ]);
317        assert_eq!(
318            m.fetch_manifest(&LayerRef::Digest("d".into())).unwrap(),
319            b"manifest".to_vec()
320        );
321        assert_eq!(m.served_by().as_deref(), Some("mirror"));
322    }
323
324    /// A mirror that has not synced this layer is as unhelpful as one that is
325    /// down, and neither says anything about the bytes.
326    // rivet: verifies REQ-MIRROR-001
327    #[test]
328    fn a_source_that_lacks_the_layer_falls_through_too() {
329        let m = mirrors(vec![("primary", empty()), ("mirror", ok(b"manifest"))]);
330        assert!(m.fetch_manifest(&LayerRef::Digest("d".into())).is_ok());
331        assert_eq!(m.served_by().as_deref(), Some("mirror"));
332    }
333
334    /// Clause 4. An operator who cannot tell they have been on a mirror for a
335    /// month cannot tell the primary has been down for a month.
336    // rivet: verifies REQ-MIRROR-001
337    #[test]
338    fn which_source_served_the_layer_is_reportable() {
339        let m = mirrors(vec![
340            ("oci://primary", down("503")),
341            ("oci://backup", ok(b"m")),
342        ]);
343        m.fetch_manifest(&LayerRef::Digest("d".into())).unwrap();
344        assert_eq!(m.served_by().as_deref(), Some("oci://backup"));
345        let attempts = m.attempts();
346        assert_eq!(attempts.len(), 1);
347        assert_eq!(attempts[0].0, "oci://primary");
348        assert!(attempts[0].1.contains("503"), "{attempts:?}");
349    }
350
351    /// When nothing answers, the error names every source and what each said —
352    /// "the registry is down" is not actionable when there were three.
353    // rivet: verifies REQ-MIRROR-001
354    #[test]
355    fn when_no_source_answers_the_error_names_all_of_them() {
356        let m = mirrors(vec![
357            ("oci://a", down("no such host")),
358            ("oci://b", down("503 Service Unavailable")),
359        ]);
360        let e = m
361            .fetch_manifest(&LayerRef::Digest("d".into()))
362            .expect_err("must fail");
363        let msg = e.to_string();
364        assert!(
365            msg.contains("oci://a") && msg.contains("no such host"),
366            "{msg}"
367        );
368        assert!(msg.contains("oci://b") && msg.contains("503"), "{msg}");
369        assert!(msg.contains("2 source(s)"), "{msg}");
370        assert!(m.served_by().is_none());
371    }
372
373    /// A realm with no sources is a configuration error, not an empty loop
374    /// that silently reports "not found".
375    // rivet: verifies REQ-MIRROR-001
376    #[test]
377    fn a_realm_with_no_sources_says_so() {
378        let m = Mirrors::new(Vec::new());
379        let e = m
380            .fetch_manifest(&LayerRef::Digest("d".into()))
381            .expect_err("must fail");
382        assert!(e.to_string().contains("no sources at all"), "{e}");
383    }
384
385    /// A later source is not consulted once an earlier one answers — order is
386    /// a stated preference, and an operator must be able to predict it.
387    // rivet: verifies REQ-MIRROR-001
388    #[test]
389    fn a_source_after_the_one_that_answered_is_not_consulted() {
390        let second = ok(b"second");
391        let counter = Rc::clone(&second.calls);
392        let m = mirrors(vec![("primary", ok(b"first")), ("mirror", second)]);
393        m.fetch_manifest(&LayerRef::Digest("d".into())).unwrap();
394        assert_eq!(counter.get(), 0, "the later source was consulted anyway");
395    }
396
397    /// Every fetch must fall through, not just the manifest. Each is a
398    /// separate delegation, and a copy-paste slip in one of them would fail
399    /// over on the manifest and then stall on the blob — an install that gets
400    /// halfway and dies is worse than one that never starts.
401    // rivet: verifies REQ-MIRROR-001
402    #[test]
403    fn every_kind_of_fetch_falls_through_not_only_the_manifest() {
404        let m = mirrors(vec![("primary", down("503")), ("mirror", ok(b"bytes"))]);
405        assert_eq!(m.fetch_blob("sha256:x").unwrap(), b"bytes".to_vec());
406        assert_eq!(m.served_by().as_deref(), Some("mirror"));
407
408        let m = mirrors(vec![("primary", down("503")), ("mirror", ok(b"index"))]);
409        assert_eq!(
410            m.fetch_line_index("2026.09").unwrap(),
411            Some(b"index".to_vec())
412        );
413        assert_eq!(m.served_by().as_deref(), Some("mirror"));
414
415        let m = mirrors(vec![("primary", down("503")), ("mirror", ok(b"att"))]);
416        assert!(m.fetch_attestations(&LayerRef::Digest("d".into())).is_ok());
417        assert_eq!(m.served_by().as_deref(), Some("mirror"));
418    }
419
420    /// ...and every kind must REFUSE when nobody answers, rather than
421    /// returning an empty result that reads as "there is none".
422    // rivet: verifies REQ-MIRROR-001
423    #[test]
424    fn every_kind_of_fetch_refuses_when_no_source_answers() {
425        let m = mirrors(vec![("a", down("no such host")), ("b", down("503"))]);
426        assert!(m.fetch_manifest(&LayerRef::Digest("d".into())).is_err());
427        assert!(m.fetch_blob("sha256:x").is_err());
428        assert!(m.fetch_line_index("2026.09").is_err());
429        assert!(m.fetch_attestations(&LayerRef::Digest("d".into())).is_err());
430        assert!(
431            m.served_by().is_none(),
432            "nothing served, yet a source is named"
433        );
434    }
435
436    /// The labels are what an operator is shown; an empty list would make
437    /// every mirror diagnostic say nothing.
438    // rivet: verifies REQ-MIRROR-001
439    #[test]
440    fn the_configured_sources_are_reportable_in_order() {
441        let m = mirrors(vec![("oci://a", ok(b"x")), ("oci://b", ok(b"y"))]);
442        assert_eq!(m.labels(), vec!["oci://a", "oci://b"]);
443        assert_eq!(Mirrors::new(Vec::new()).labels(), Vec::<&str>::new());
444    }
445
446    /// `Ok(None)` from every source is a legitimate absence — line-status is
447    /// updatable evidence and some layers have none. That must not be reported
448    /// as "no source could be reached".
449    // rivet: verifies REQ-MIRROR-001
450    #[test]
451    fn a_document_no_source_carries_is_absent_not_unreachable() {
452        let m = mirrors(vec![("a", empty()), ("b", empty())]);
453        assert_eq!(
454            m.fetch_line_status(&LayerRef::Digest("d".into())).unwrap(),
455            None
456        );
457    }
458
459    /// ...but if nobody could be REACHED, that is not absence.
460    // rivet: verifies REQ-MIRROR-001
461    #[test]
462    fn a_document_nobody_could_be_asked_about_is_not_reported_as_absent() {
463        let m = mirrors(vec![("a", down("no such host")), ("b", down("503"))]);
464        let e = m
465            .fetch_line_status(&LayerRef::Digest("d".into()))
466            .expect_err("must not report absence");
467        assert!(e.to_string().contains("could be reached"), "{e}");
468    }
469
470    /// A source that carries the status is preferred over one that does not,
471    /// even when the one that does not comes first.
472    // rivet: verifies REQ-MIRROR-001
473    #[test]
474    fn a_source_carrying_the_document_is_reached_past_one_that_does_not() {
475        let m = mirrors(vec![("a", empty()), ("b", ok(b"status"))]);
476        assert_eq!(
477            m.fetch_line_status(&LayerRef::Digest("d".into())).unwrap(),
478            Some(b"status".to_vec())
479        );
480        assert_eq!(m.served_by().as_deref(), Some("b"));
481    }
482}