archive — guide
Class:
guide· audience: a techie packing or reading bundles. The what is inarchive-design.md; the why inarchive-reasoning.md.
Pick an engine
use skidbladnir::archive::{Archive, TarZstd};
let eng = TarZstd::new(); // always available, always offline
For the REAL znippy-core engine (seal/read/verify/unpack via ../znippy, so the bundle is the one canonical .znippy — znippy CLI / DuckDB readable), build with --features znippy and swap the constructor — the trait seam means nothing else changes:
# #[cfg(feature = "znippy")]
let eng = skidbladnir::archive::ZnippyArchive::new();
Pack a staging tree
// staging_root holds your files under their relative paths (see pack-guide.md)
let manifest = eng.pack(&staging_root, "20260704", &bundle_path)?; // Archive::pack
assert!(manifest.verify_root()); // self-check
// a sidecar <bundle_path>.manifest.json is written beside the bundle
manifest.entries is one EntryDigest { path, sha256, len } per file; manifest.root_sha256 is the hash over all of them. This manifest is what the far side verifies.
List and read without extracting everything
for path in eng.list(&bundle_path)? { println!("{path}"); }
let bytes = eng.read_entry(&bundle_path, "bin/app")?; // znippy: get_file random-access; tar: temp-extract
Verify a received bundle
let manifest = skidbladnir::archive::load_manifest(&bundle_path)?;
let bad = eng.verify_entries(&bundle_path, &manifest)?; // Vec<String> of bad paths
assert!(bad.is_empty(), "tampered or incomplete: {bad:?}");
Prefer the fetch wrapper for the full receipt gate (manifest root + optional out-of-band SHA + per-entry) — see move-guide.md.
Ship only what changed (znippy delta)
With --features znippy, pack a patch against the previous manifest and apply it on the far side:
# #[cfg(feature = "znippy")] {
let patch = eng.delta(&old_manifest, &new_staging, "20260705")?; // ships only changed blobs
println!("shipped {} bytes, reused {} entries", patch.shipped_bytes(), patch.reused());
let new_manifest = eng.apply(&old_bundle, &patch, &new_bundle)?; // re-seals a real znippy bundle; same content root as a fresh pack
# }
On the default TarZstd engine delta/apply bail with a clear "use the znippy feature" message (archive.rs:143) — they are not silently faked.
Gotchas
- The manifest is a sidecar file (
<bundle>.manifest.json), not inside the archive. Ship it alongside the bundle, or regenerate it withpack. TarZstd::new()maps level 0 to zstd level 3; pass a level explicitly for a different ratio.- znippy
read_entry(get_file) bounds blob length against file size before allocating — a corrupt/hostile index cannot OOM you — and blake3-verifies every chunk. Extraction rejects../absolute paths (zip-slip), and a tampered byte fails verification loudly.