weft-core 0.1.1

OpenAI-compatible AI agent runtime with WASM capability plugin system
Documentation
use crate::app::config::AppLockPackage;
use crate::app::generation::ValidationResult;
use crate::app::policy::AppProfile;
use sha2::{Digest, Sha512};
use std::path::{Path, PathBuf};

const STORE_CHECK_PREFIX: &str = "store-policy:";

pub fn verify_local_store_packages(
    instance_dir: Option<&Path>,
    packages: &[AppLockPackage],
    profile: AppProfile,
) -> Vec<ValidationResult> {
    packages
        .iter()
        .map(|package| verify_local_store_package(instance_dir, package, profile))
        .collect()
}

pub fn is_store_check(result: &ValidationResult) -> bool {
    result.check.starts_with(STORE_CHECK_PREFIX)
}

fn verify_local_store_package(
    instance_dir: Option<&Path>,
    package: &AppLockPackage,
    profile: AppProfile,
) -> ValidationResult {
    let identity = package.identity();
    let check = format!("{STORE_CHECK_PREFIX}{identity}");
    let metadata_present = !package.store_path.trim().is_empty();

    if !metadata_present {
        return ValidationResult {
            check,
            passed: true,
            message: format!(
                "Warning: package '{}' has no explicit local store path metadata in the generation lock; offline store verification was skipped for backward compatibility under profile '{}'",
                identity,
                profile.as_str()
            ),
        };
    }

    let store_path = package.store_path.trim();

    let resolved_path = resolve_store_path(instance_dir, store_path);
    let resolved_display = resolved_path.display().to_string();
    if !resolved_path.exists() {
        return policy_result(
            profile,
            check,
            format!(
                "Package '{}' requires local store object '{}' at '{}' but the path is missing",
                identity, package.store_object_id, resolved_display
            ),
            format!(
                "Warning: package '{}' requires local store object '{}' at '{}' but the path is missing; developer profile surfaced the offline store verification warning without failing activation",
                identity, package.store_object_id, resolved_display
            ),
        );
    }

    let expected_digest = expected_store_sha512(package);
    match expected_digest {
        Some(expected) => match compute_store_sha512(&resolved_path) {
            Ok(actual) if actual == expected => ValidationResult {
                check,
                passed: true,
                message: format!(
                    "Package '{}' local store path '{}' exists and matches expected store digest '{}{}'",
                    identity,
                    resolved_display,
                    if package.store_object_id.starts_with("store:sha512:") {
                        "store:sha512:"
                    } else {
                        ""
                    },
                    expected
                ),
            },
            Ok(actual) => policy_result(
                profile,
                check,
                format!(
                    "Package '{}' local store path '{}' exists but its computed sha512 '{}' does not match expected '{}'",
                    identity, resolved_display, actual, expected
                ),
                format!(
                    "Warning: package '{}' local store path '{}' exists but its computed sha512 '{}' does not match expected '{}'; developer profile surfaced the mismatch without failing activation",
                    identity, resolved_display, actual, expected
                ),
            ),
            Err(error) => policy_result(
                profile,
                check,
                format!(
                    "Package '{}' local store path '{}' exists but sha512 verification failed: {}",
                    identity, resolved_display, error
                ),
                format!(
                    "Warning: package '{}' local store path '{}' exists but sha512 verification failed: {}; developer profile surfaced the verification issue without failing activation",
                    identity, resolved_display, error
                ),
            ),
        },
        None => ValidationResult {
            check,
            passed: true,
            message: format!(
                "Package '{}' local store path '{}' exists; no comparable sha512 store digest metadata was present, so offline verification recorded existence only",
                identity, resolved_display
            ),
        },
    }
}

fn policy_result(
    profile: AppProfile,
    check: String,
    strict_message: String,
    developer_message: String,
) -> ValidationResult {
    match profile {
        AppProfile::Developer => ValidationResult {
            check,
            passed: true,
            message: developer_message,
        },
        AppProfile::Safe | AppProfile::Trusted => ValidationResult {
            check,
            passed: false,
            message: strict_message,
        },
    }
}

fn expected_store_sha512(package: &AppLockPackage) -> Option<String> {
    if let Some(value) = package.store_object_id.trim().strip_prefix("store:sha512:") {
        if is_valid_sha512_hex(value) {
            return Some(value.to_string());
        }
    }

    let sha512 = package.sha512.trim();
    if is_valid_sha512_hex(sha512) {
        return Some(sha512.to_string());
    }

    None
}

fn is_valid_sha512_hex(value: &str) -> bool {
    value.len() == 128 && value.chars().all(|c| c.is_ascii_hexdigit())
}

fn resolve_store_path(instance_dir: Option<&Path>, store_path: &str) -> PathBuf {
    let store_path = Path::new(store_path);
    if store_path.is_absolute() || instance_dir.is_none() {
        return store_path.to_path_buf();
    }

    let instance_dir = instance_dir.expect("checked above");
    let mut candidates = vec![instance_dir.join(store_path)];
    if let Some(weft_dir) = instance_dir.parent() {
        candidates.push(weft_dir.join(store_path));
        if let Some(repo_root) = weft_dir.parent() {
            candidates.push(repo_root.join(store_path));
        }
    }

    candidates
        .iter()
        .find(|candidate| candidate.exists())
        .cloned()
        .unwrap_or_else(|| {
            if store_path
                .components()
                .next()
                .is_some_and(|component| component.as_os_str() == ".weft")
            {
                if let Some(repo_root) = instance_dir.parent().and_then(Path::parent) {
                    return repo_root.join(store_path);
                }
            }

            instance_dir.join(store_path)
        })
}

fn compute_store_sha512(path: &Path) -> Result<String, std::io::Error> {
    let mut hasher = Sha512::new();

    fn update_dir(hasher: &mut Sha512, root: &Path, path: &Path) -> Result<(), std::io::Error> {
        let mut entries = std::fs::read_dir(path)?
            .filter_map(|entry| entry.ok())
            .map(|entry| entry.path())
            .collect::<Vec<_>>();
        entries.sort();

        for entry in entries {
            let relative = entry
                .strip_prefix(root)
                .unwrap_or(&entry)
                .to_string_lossy()
                .replace('\\', "/");
            if entry.is_dir() {
                hasher.update(relative.as_bytes());
                update_dir(hasher, root, &entry)?;
            } else if entry.is_file() {
                hasher.update(relative.as_bytes());
                hasher.update(std::fs::read(&entry)?);
            }
        }

        Ok(())
    }

    if path.is_file() {
        hasher.update(std::fs::read(path)?);
    } else if path.is_dir() {
        update_dir(&mut hasher, path, path)?;
    }

    Ok(format!("{:x}", hasher.finalize()))
}

#[cfg(test)]
mod tests {
    use super::verify_local_store_packages;
    use crate::app::{AppLockEvidence, AppLockPackage, AppProfile};
    use sha2::{Digest, Sha512};
    use tempfile::tempdir;

    fn store_package(
        name: &str,
        store_object_id: &str,
        store_path: &str,
        sha512: &str,
    ) -> AppLockPackage {
        AppLockPackage {
            name: name.into(),
            version: "0.1.0".into(),
            store_object_id: store_object_id.into(),
            store_path: store_path.into(),
            sha512: sha512.into(),
            evidence: AppLockEvidence::default(),
            ..AppLockPackage::default()
        }
    }

    #[test]
    fn existing_store_path_passes_when_sha512_matches() {
        let root = tempdir().expect("tempdir");
        let instance_dir = root.path().join(".weft").join("weft-claw");
        let store_path = root
            .path()
            .join(".weft")
            .join("store")
            .join("agent-runtime-0.1.0");
        std::fs::create_dir_all(store_path.parent().expect("parent")).expect("store dir");
        std::fs::write(&store_path, b"store-bytes").expect("store file");
        let digest = format!("{:x}", Sha512::digest(b"store-bytes"));

        let results = verify_local_store_packages(
            Some(&instance_dir),
            &[store_package(
                "agent-runtime",
                &format!("store:sha512:{digest}"),
                ".weft/store/agent-runtime-0.1.0",
                &digest,
            )],
            AppProfile::Safe,
        );

        assert_eq!(results.len(), 1);
        assert!(results[0].passed);
        assert!(results[0].message.contains("matches expected store digest"));
    }

    #[test]
    fn missing_store_path_fails_for_safe_and_trusted_profiles() {
        let root = tempdir().expect("tempdir");
        let instance_dir = root.path().join(".weft").join("weft-claw");
        let package = store_package(
            "agent-runtime",
            "store:sha512:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            ".weft/store/missing-agent-runtime-0.1.0",
            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
        );

        for profile in [AppProfile::Safe, AppProfile::Trusted] {
            let results = verify_local_store_packages(
                Some(&instance_dir),
                std::slice::from_ref(&package),
                profile,
            );
            assert!(!results[0].passed);
            assert!(results[0].message.contains("path is missing"));
        }
    }

    #[test]
    fn developer_missing_store_path_warns_without_failing() {
        let root = tempdir().expect("tempdir");
        let instance_dir = root.path().join(".weft").join("weft-claw");
        let results = verify_local_store_packages(
            Some(&instance_dir),
            &[store_package(
                "agent-runtime",
                "store:sha512:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
                ".weft/store/missing-agent-runtime-0.1.0",
                "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
            )],
            AppProfile::Developer,
        );

        assert!(results[0].passed);
        assert!(results[0].message.contains("Warning:"));
        assert!(results[0].message.contains("missing"));
    }

    #[test]
    fn absent_store_metadata_stays_backward_compatible_warning() {
        let results = verify_local_store_packages(
            None,
            &[store_package("agent-runtime", "", "", "")],
            AppProfile::Trusted,
        );

        assert!(results[0].passed);
        assert!(results[0].message.contains("Warning:"));
        assert!(results[0].message.contains("backward compatibility"));
    }
}