bsv-wallet-cli 0.2.8

Self-hosted BSV wallet CLI and BRC-100 server, wire-compatible with MetaNet Client
Documentation
use anyhow::{anyhow, Result};
use bsv_sdk::primitives::sha256;
use std::path::{Path, PathBuf};
use std::process::Command;

pub async fn run(db_path: &str, to: Option<PathBuf>) -> Result<()> {
    if !Path::new(".env").exists() {
        return Err(anyhow!(
            ".env not found in current directory; run `bsv-wallet init` first or cd to the wallet dir"
        ));
    }
    if !Path::new(db_path).exists() {
        return Err(anyhow!("{} not found", db_path));
    }

    let out_path = to.unwrap_or_else(|| {
        let ts = chrono::Local::now().format("%Y%m%d-%H%M%S");
        PathBuf::from(format!("bsv-wallet-backup-{}.tar.gz", ts))
    });

    let mut files: Vec<String> = vec![".env".to_string(), db_path.to_string()];
    let stem = db_path.trim_end_matches(".db");
    for suffix in ["-wal", "-shm"] {
        let p = format!("{}.db{}", stem, suffix);
        if Path::new(&p).exists() {
            files.push(p);
        }
    }

    let status = Command::new("tar")
        .arg("-czf")
        .arg(&out_path)
        .args(&files)
        .status()
        .map_err(|e| anyhow!("failed to spawn tar: {}", e))?;
    if !status.success() {
        return Err(anyhow!("tar exited with status {:?}", status));
    }

    let bytes = std::fs::read(&out_path)?;
    let digest = hex::encode(sha256(&bytes));
    let size_kb = bytes.len() as f64 / 1024.0;

    println!("Backup written: {}", out_path.display());
    println!("Contents: {}", files.join(", "));
    println!("Size: {:.1} KB", size_kb);
    println!("SHA256: {}", digest);
    println!();
    println!("This tarball is plaintext. Encrypt it at rest before storing, e.g.:");
    println!(
        "  age -p -o {}.age {}",
        out_path.display(),
        out_path.display()
    );
    println!("Or store inside an encrypted vault (1Password, encrypted disk image, etc.).");

    Ok(())
}