242 lines
9.5 KiB
Rust
242 lines
9.5 KiB
Rust
//! Exotic Capability: Self-Booting RVF Microservice
|
|
//!
|
|
//! Demonstrates embedding a kernel image into an RVF file, turning it
|
|
//! into a self-booting cognitive container that contains both data AND runtime.
|
|
//!
|
|
//! Features:
|
|
//! - Create a store with vectors
|
|
//! - Create a synthetic kernel image (constructed unikernel binary)
|
|
//! - Embed using store.embed_kernel()
|
|
//! - Extract the kernel and verify header fields
|
|
//! - Witness chain for boot sequence audit
|
|
//! - Show that the file contains data + runtime
|
|
//!
|
|
//! RVF segments used: VEC_SEG, KERNEL_SEG, MANIFEST_SEG, WITNESS_SEG
|
|
//!
|
|
//! Run: cargo run --example self_booting
|
|
|
|
use rvf_runtime::{QueryOptions, RvfOptions, RvfStore};
|
|
use rvf_runtime::options::DistanceMetric;
|
|
use rvf_kernel;
|
|
use rvf_types::kernel::{KernelArch, KernelHeader, KernelType, KERNEL_MAGIC};
|
|
use rvf_crypto::{create_witness_chain, verify_witness_chain, shake256_256, WitnessEntry};
|
|
use tempfile::TempDir;
|
|
|
|
/// Simple LCG-based pseudo-random vector generator for deterministic results.
|
|
fn random_vector(dim: usize, seed: u64) -> Vec<f32> {
|
|
let mut v = Vec::with_capacity(dim);
|
|
let mut x = seed.wrapping_add(1);
|
|
for _ in 0..dim {
|
|
x = x.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
|
|
v.push(((x >> 33) as f32) / (u32::MAX as f32) - 0.5);
|
|
}
|
|
v
|
|
}
|
|
|
|
fn main() {
|
|
println!("=== Self-Booting RVF Microservice ===\n");
|
|
|
|
let dim = 128;
|
|
let num_vectors = 50;
|
|
|
|
// ====================================================================
|
|
// 1. Create store with vectors
|
|
// ====================================================================
|
|
println!("--- 1. Create Store with Vector Data ---");
|
|
|
|
let tmp_dir = TempDir::new().expect("failed to create temp dir");
|
|
let store_path = tmp_dir.path().join("bootable.rvf");
|
|
|
|
let options = RvfOptions {
|
|
dimension: dim as u16,
|
|
metric: DistanceMetric::L2,
|
|
..Default::default()
|
|
};
|
|
|
|
let mut store = RvfStore::create(&store_path, options).expect("failed to create store");
|
|
|
|
let vectors: Vec<Vec<f32>> = (0..num_vectors)
|
|
.map(|i| random_vector(dim, i as u64))
|
|
.collect();
|
|
let vec_refs: Vec<&[f32]> = vectors.iter().map(|v| v.as_slice()).collect();
|
|
let ids: Vec<u64> = (0..num_vectors as u64).collect();
|
|
|
|
let ingest = store
|
|
.ingest_batch(&vec_refs, &ids, None)
|
|
.expect("ingest failed");
|
|
println!(" Ingested {} vectors ({} dims)", ingest.accepted, dim);
|
|
|
|
// Verify query works before kernel embedding
|
|
let query = random_vector(dim, 25);
|
|
let results = store
|
|
.query(&query, 5, &QueryOptions::default())
|
|
.expect("query failed");
|
|
println!(" Pre-kernel query: top-5 results OK (nearest ID={})", results[0].id);
|
|
|
|
// ====================================================================
|
|
// 2. Create a synthetic kernel image
|
|
// ====================================================================
|
|
println!("\n--- 2. Synthetic Kernel Image ---");
|
|
|
|
// Build a real kernel (Docker) or fall back to builtin stub
|
|
let tmpdir = std::env::temp_dir().join("rvf-self-boot-build");
|
|
std::fs::create_dir_all(&tmpdir).ok();
|
|
let built = rvf_kernel::KernelBuilder::new(KernelArch::X86_64)
|
|
.with_initramfs(&["rvf-server"])
|
|
.build(&tmpdir)
|
|
.expect("build kernel");
|
|
let kernel_image = built.bzimage;
|
|
let kernel_label = if kernel_image.len() > 8192 { "real bzImage" } else { "builtin stub" };
|
|
|
|
println!(" Kernel image size: {} bytes ({})", kernel_image.len(), kernel_label);
|
|
println!(" Kernel type: HermitOS (unikernel)");
|
|
println!(" Target arch: x86_64");
|
|
println!(" API port: 8080");
|
|
|
|
// ====================================================================
|
|
// 3. Embed kernel into the RVF file
|
|
// ====================================================================
|
|
println!("\n--- 3. Embed Kernel (KERNEL_SEG) ---");
|
|
|
|
let seg_id = store
|
|
.embed_kernel(
|
|
KernelArch::X86_64 as u8,
|
|
KernelType::Hermit as u8,
|
|
0x0018, // HAS_QUERY_API | HAS_NETWORKING
|
|
&kernel_image,
|
|
8080,
|
|
Some("console=ttyS0 quiet rvf.listen=0.0.0.0:8080"),
|
|
)
|
|
.expect("failed to embed kernel");
|
|
|
|
println!(" Kernel embedded as segment ID: {}", seg_id);
|
|
|
|
let status = store.status();
|
|
println!(" Store file size: {} bytes", status.file_size);
|
|
println!(" Total segments: {}", status.total_segments);
|
|
|
|
// ====================================================================
|
|
// 4. Extract kernel and verify header
|
|
// ====================================================================
|
|
println!("\n--- 4. Extract and Verify Kernel ---");
|
|
|
|
let (header_bytes, image_bytes) = store
|
|
.extract_kernel()
|
|
.expect("extract_kernel failed")
|
|
.expect("no kernel segment found");
|
|
|
|
assert_eq!(header_bytes.len(), 128, "kernel header should be 128 bytes");
|
|
|
|
// Parse the kernel header
|
|
let header_arr: [u8; 128] = header_bytes.try_into().expect("header size mismatch");
|
|
let kernel_header = KernelHeader::from_bytes(&header_arr).expect("invalid kernel header");
|
|
|
|
println!(" Header verification:");
|
|
println!(" Magic: 0x{:08X} (expected: 0x{:08X}) {}",
|
|
kernel_header.kernel_magic,
|
|
KERNEL_MAGIC,
|
|
if kernel_header.kernel_magic == KERNEL_MAGIC { "OK" } else { "FAIL" }
|
|
);
|
|
println!(" Arch: {} (x86_64=0x{:02X})",
|
|
kernel_header.arch,
|
|
KernelArch::X86_64 as u8
|
|
);
|
|
println!(" Type: {} (Hermit=0x{:02X})",
|
|
kernel_header.kernel_type,
|
|
KernelType::Hermit as u8
|
|
);
|
|
println!(" API port: {}", kernel_header.api_port);
|
|
println!(" Image size: {} bytes", kernel_header.image_size);
|
|
println!(" Image hash: {}...", hex_string(&kernel_header.image_hash[..16]));
|
|
println!(" Cmdline offset: {}", kernel_header.cmdline_offset);
|
|
println!(" Cmdline length: {}", kernel_header.cmdline_length);
|
|
|
|
assert_eq!(kernel_header.kernel_magic, KERNEL_MAGIC);
|
|
assert_eq!(kernel_header.arch, KernelArch::X86_64 as u8);
|
|
assert_eq!(kernel_header.kernel_type, KernelType::Hermit as u8);
|
|
assert_eq!(kernel_header.api_port, 8080);
|
|
assert_eq!(kernel_header.image_size, kernel_image.len() as u64);
|
|
|
|
// Verify the image matches
|
|
assert!(
|
|
image_bytes.starts_with(&kernel_image),
|
|
"extracted image does not match original"
|
|
);
|
|
println!("\n Image verification: extracted image matches original.");
|
|
|
|
// ====================================================================
|
|
// 5. Verify queries still work alongside kernel
|
|
// ====================================================================
|
|
println!("\n--- 5. Co-existence Verification ---");
|
|
|
|
let results_after = store
|
|
.query(&query, 5, &QueryOptions::default())
|
|
.expect("query failed after kernel embed");
|
|
|
|
println!(" Post-kernel query: top-5 results OK (nearest ID={})", results_after[0].id);
|
|
assert_eq!(results[0].id, results_after[0].id, "query results should match");
|
|
println!(" Query results match pre-kernel: VERIFIED");
|
|
|
|
// ====================================================================
|
|
// 6. Witness chain for boot sequence
|
|
// ====================================================================
|
|
println!("\n--- 6. Boot Sequence Witness Chain ---");
|
|
|
|
let boot_steps = [
|
|
("vmm_init", 0x01u8),
|
|
("kernel_load", 0x02),
|
|
("memory_map", 0x02),
|
|
("rvf_mount", 0x02),
|
|
("api_listen", 0x01),
|
|
];
|
|
|
|
let entries: Vec<WitnessEntry> = boot_steps
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(i, (step, wtype))| {
|
|
let action_data = format!("boot:{}:{}", step, i);
|
|
WitnessEntry {
|
|
prev_hash: [0u8; 32],
|
|
action_hash: shake256_256(action_data.as_bytes()),
|
|
timestamp_ns: 1_700_000_000_000_000_000 + i as u64 * 100_000_000,
|
|
witness_type: *wtype,
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
let chain_bytes = create_witness_chain(&entries);
|
|
let verified = verify_witness_chain(&chain_bytes).expect("chain verification failed");
|
|
println!(" Boot chain: {} entries, VALID", verified.len());
|
|
|
|
println!("\n Boot sequence:");
|
|
for (i, (step, _)) in boot_steps.iter().enumerate() {
|
|
let wtype_name = match verified[i].witness_type {
|
|
0x01 => "PROV",
|
|
0x02 => "COMP",
|
|
_ => "????",
|
|
};
|
|
println!(" [{}] {} -> {}", wtype_name, i, step);
|
|
}
|
|
|
|
// ====================================================================
|
|
// 7. Show what the file contains
|
|
// ====================================================================
|
|
println!("\n--- 7. Self-Booting File Contents ---");
|
|
let final_status = store.status();
|
|
println!(" The single .rvf file now contains:");
|
|
println!(" - {} vectors ({}-dim embeddings)", final_status.total_vectors, dim);
|
|
println!(" - HermitOS x86_64 kernel image ({} bytes)", kernel_image.len());
|
|
println!(" - Query API on port 8080");
|
|
println!(" - {} segments total", final_status.total_segments);
|
|
println!(" - File size: {} bytes", final_status.file_size);
|
|
println!("\n This file can serve queries WITHOUT any external runtime.");
|
|
println!(" Just boot it: firecracker --kernel bootable.rvf");
|
|
|
|
store.close().expect("failed to close store");
|
|
println!("\nDone.");
|
|
}
|
|
|
|
fn hex_string(bytes: &[u8]) -> String {
|
|
bytes.iter().map(|b| format!("{:02x}", b)).collect()
|
|
}
|