ive harnessed the harness
Something went wrong. Try again.
5.8 kB · 175 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176//! Content-addressed behavior snapshots, separate from activation authority.use crate::protocol::HookSlot;use anyhow::{ensure, Context, Result};use serde::Deserialize;use sha2::{Digest, Sha256};use std::{ collections::BTreeMap, fs, io::Write, path::{Path, PathBuf},};
#[derive(Clone, Debug)]pub struct Release { pub id: String, pub path: PathBuf, pub hooks: BTreeMap<HookSlot, String>,}
#[derive(Deserialize)]#[serde(deny_unknown_fields)]struct Manifest { version: u16, hooks: BTreeMap<HookSlot, String>,}
fn collect( root: &Path, directory: &Path, entries: &mut Vec<(String, Vec<u8>)>, bytes: &mut usize,) -> Result<()> { for entry in fs::read_dir(directory)? { let entry = entry?; let metadata = entry.file_type()?; ensure!( !metadata.is_symlink(), "behavior snapshots cannot contain symlinks" ); if metadata.is_dir() { collect(root, &entry.path(), entries, bytes)?; continue; } ensure!(metadata.is_file(), "behavior entry must be a regular file"); let path = entry.path(); ensure!( matches!( path.extension().and_then(|s| s.to_str()), Some("py" | "json" | "md") ), "unsupported behavior file" ); ensure!( entry.metadata()?.len() <= 2_097_152, "behavior file too large" ); let content = fs::read(&path)?; *bytes += content.len(); ensure!( *bytes <= 2_097_152 && entries.len() < 128, "behavior snapshot exceeds limits" ); let name = path .strip_prefix(root)? .to_str() .context("non-UTF8 behavior path")? .replace('\\', "/"); entries.push((name, content)); } Ok(())}
type BehaviorRead = (String, Vec<(String, Vec<u8>)>, Manifest);
fn read(root: &Path) -> Result<BehaviorRead> { ensure!( !fs::symlink_metadata(root)?.file_type().is_symlink(), "behavior root cannot be a symlink" ); let mut entries = Vec::new(); let mut total = 0; collect(root, root, &mut entries, &mut total)?; entries.sort_by(|a, b| a.0.cmp(&b.0)); let mut digest = Sha256::new(); for (name, content) in &entries { digest.update((name.len() as u64).to_be_bytes()); digest.update(name.as_bytes()); digest.update((content.len() as u64).to_be_bytes()); digest.update(content); } let manifest: Manifest = serde_json::from_slice( &entries .iter() .find(|(name, _)| name == "manifest.json") .context("missing manifest.json")? .1, )?; ensure!( manifest.version == 1, "unsupported behavior manifest version" ); ensure!( manifest.hooks.len() == 2 && manifest.hooks.contains_key(&HookSlot::Attention) && manifest.hooks.contains_key(&HookSlot::Delivery), "both implemented hook slots are required" ); Ok((format!("{:x}", digest.finalize()), entries, manifest))}
impl Release { /// Snapshot draft bytes once, then atomically publish their directory. No /// active pointer is changed here. Deployment must protect published paths. pub fn publish(source: &Path, releases: &Path) -> Result<Self> { let (id, entries, _) = read(source)?; fs::create_dir_all(releases)?; let destination = releases.join(&id); if !destination.exists() { let candidate = tempfile::Builder::new() .prefix(".candidate-") .tempdir_in(releases)?; for (name, content) in entries { let path = candidate.path().join(name); fs::create_dir_all(path.parent().context("file has no parent")?)?; let mut file = fs::File::create(&path)?; file.write_all(&content)?; file.sync_all()?; } sync_directories(candidate.path())?; // fs::rename is atomic within this directory. A competing publisher // may have won; verify its content rather than overwrite blindly. if let Err(error) = fs::rename(candidate.path(), &destination) { if !destination.is_dir() { return Err(error.into()); } } } fs::File::open(releases)?.sync_all()?; let release = Self::load(&destination)?; ensure!(release.id == id, "existing published snapshot has changed"); Ok(release) }
pub fn load(path: impl AsRef<Path>) -> Result<Self> { let path = path.as_ref(); let (id, _, manifest) = read(path)?; if let Some(dir_name) = path.file_name().and_then(|n| n.to_str()) { if dir_name.len() == 64 && dir_name.chars().all(|c| c.is_ascii_hexdigit()) { ensure!( dir_name == id, "tampered release bytes: directory name {dir_name} does not match computed hash {id}" ); } } Ok(Self { id, path: path.canonicalize()?, hooks: manifest.hooks, }) }}
// Files were synced when written. Sync directory entries bottom-up before// publishing, then sync the release parent after rename. Actual crash durability// remains subject to the filesystem and storage stack.fn sync_directories(path: &Path) -> Result<()> { for entry in fs::read_dir(path)? { let entry = entry?; if entry.file_type()?.is_dir() { sync_directories(&entry.path())?; } } fs::File::open(path)?.sync_all()?; Ok(())}