Skip to main content

kernel/manifests/
provenance.rs

1//! Where an installed runtime came from — the `.provenance.json` the installer
2//! stamps beside a manifest so the loader can tell community runtimes (which
3//! must run contained) from first-party ones.
4
5use std::path::Path;
6
7use serde::{Deserialize, Serialize};
8
9use crate::persistence::{self, StoreError};
10use crate::time::now_millis;
11
12/// The origin marking a community-installed runtime.
13pub const COMMUNITY_ORIGIN: &str = "community";
14const FILE_NAME: &str = ".provenance.json";
15
16/// The provenance of an installed runtime.
17#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
18pub struct RuntimeProvenance {
19    pub origin: String,
20    /// Install time, epoch milliseconds.
21    pub installed_at: i64,
22}
23
24impl RuntimeProvenance {
25    /// A provenance for `origin`, stamped installed-now.
26    pub fn new(origin: impl Into<String>) -> Self {
27        Self {
28            origin: origin.into(),
29            installed_at: now_millis(),
30        }
31    }
32
33    /// A provenance marking a community install.
34    pub fn community() -> Self {
35        Self::new(COMMUNITY_ORIGIN)
36    }
37
38    /// Whether this marks a community runtime (which must run contained).
39    pub fn is_community(&self) -> bool {
40        self.origin == COMMUNITY_ORIGIN
41    }
42
43    /// Read the provenance from `directory`, or `None` if absent or unreadable.
44    /// Unlike the quarantining store reads, a malformed provenance is left in
45    /// place (reading it must not mutate the runtime's directory).
46    pub fn read(directory: &Path) -> Option<Self> {
47        let bytes = std::fs::read(directory.join(FILE_NAME)).ok()?;
48        serde_json::from_slice(&bytes).ok()
49    }
50
51    /// Write the provenance into `directory`.
52    pub fn write(&self, directory: &Path) -> Result<(), StoreError> {
53        persistence::write_json_atomic(&directory.join(FILE_NAME), self)
54    }
55}