Skip to main content

ownership/
ownership.rs

1//! Ownership verifier.
2//!
3//! Destructive replacement requires `Owned`, or `Absent` with explicit
4//! creation authorization. This example proves ownership by exact prior
5//! content and forbids creation of new paths. Run with
6//! `cargo run -p eggup-core --example ownership`.
7
8use eggup_core::{
9    AbsentPolicy, AllValidators, ArtifactMember, ArtifactSet, CommitOwnership, ExactDigestVerifier,
10    InstallPlan, IntegrityRequirement, MemberId, ProductId, ReleaseId, TransactionDisposition,
11};
12use std::fs;
13use std::path::PathBuf;
14use std::time::{SystemTime, UNIX_EPOCH};
15
16fn temp_root(prefix: &str) -> PathBuf {
17    let nanos = SystemTime::now()
18        .duration_since(UNIX_EPOCH)
19        .map(|d| d.as_nanos())
20        .unwrap_or(0);
21    std::env::temp_dir().join(format!("{prefix}-{}-{nanos}", std::process::id()))
22}
23
24fn main() -> Result<(), Box<dyn std::error::Error>> {
25    let base = temp_root("eggup-ownership");
26    let inputs = base.join("inputs");
27    let root = base.join("install");
28    fs::create_dir_all(&inputs)?;
29    fs::create_dir_all(root.join("bin"))?;
30    fs::write(inputs.join("app"), b"new-app")?;
31    fs::write(root.join("bin/app"), b"old-app")?;
32    let new_digest = eggup_core::hash_file(&inputs.join("app"))?;
33    let old_digest = eggup_core::hash_file(&root.join("bin/app"))?;
34    let member = ArtifactMember::new(MemberId::new("main")?, inputs.join("app"), "bin/app")?
35        .with_integrity(IntegrityRequirement::Sha256(new_digest));
36    let plan = InstallPlan::new(
37        ProductId::new("demo")?,
38        ReleaseId::new("4.0.0")?,
39        &root,
40        ArtifactSet::single(member)?,
41    )?;
42    // Prove the live file is the expected old deployment; deny creation of
43    // absent destinations (replacement-only policy).
44    let verifier = ExactDigestVerifier::new(vec![(MemberId::new("main")?, old_digest)]);
45    let receipt = plan
46        .prepare()?
47        .verify_integrity()?
48        .validate(&AllValidators::new())?
49        .commit(CommitOwnership::new(&verifier, AbsentPolicy::DenyCreate))?;
50    assert_eq!(receipt.disposition(), TransactionDisposition::Committed);
51    assert_eq!(fs::read(root.join("bin/app"))?, b"new-app");
52    let _ = fs::remove_dir_all(&base);
53    println!("ownership-verified commit: {:?}", receipt.disposition());
54    Ok(())
55}