From a88534bb80c75206b4f239ffabbd11dccfda80aa Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 15 Feb 2026 18:47:56 +0000 Subject: [PATCH] feat(adr-036): AGI cognitive container types + builder Wire format for packaging the entire AGI framework into a single RVF: Types (rvf-types/src/agi_container.rs): - AgiContainerHeader: 64-byte repr(C) header (RVAG magic) - ContainerSegments: inventory of KERNEL/WASM/VEC/INDEX/WITNESS segments - ExecutionMode: Replay / Verify / Live - 16 TLV tags: model_id, policy, orchestrator, tools, eval, skills, replay_script, kernel_config, coherence, project_instructions, etc. - 12 capability flags: kernel, wasm, orchestrator, world_model, eval, skills, witness, signed, replay, offline, tools, coherence_gates Builder (rvf-runtime/src/agi_container.rs): - AgiContainerBuilder: fluent API for assembling container manifests - ParsedAgiManifest: zero-copy parser with section extraction - HMAC-SHA256 signing for tamper detection - Segment validation per execution mode Container layout: - META segment: AGI manifest (header + TLV config) - KERNEL_SEG: micro Linux kernel (Firecracker vmlinux) - WASM_SEG: interpreter + microkernel modules - VEC_SEG + INDEX_SEG: RuVector world model - WITNESS_SEG: ADR-035 witness chains - CRYPTO_SEG: signing keys and attestation Tests: 13 types + 4 runtime = 17 new tests. All 468 passing. https://claude.ai/code/session_01RnwD4x5cbpB7FPvoyYQz8G --- crates/rvf/rvf-runtime/Cargo.toml | 4 + crates/rvf/rvf-runtime/src/agi_container.rs | 564 ++++++++++++++++++++ crates/rvf/rvf-runtime/src/lib.rs | 4 + crates/rvf/rvf-types/src/agi_container.rs | 480 +++++++++++++++++ crates/rvf/rvf-types/src/lib.rs | 8 + 5 files changed, 1060 insertions(+) create mode 100644 crates/rvf/rvf-runtime/src/agi_container.rs create mode 100644 crates/rvf/rvf-types/src/agi_container.rs diff --git a/crates/rvf/rvf-runtime/Cargo.toml b/crates/rvf/rvf-runtime/Cargo.toml index 97ad8232..5270ce2b 100644 --- a/crates/rvf/rvf-runtime/Cargo.toml +++ b/crates/rvf/rvf-runtime/Cargo.toml @@ -24,3 +24,7 @@ rvf-types = { version = "0.1.0", path = "../rvf-types", features = ["std"] } [dev-dependencies] tempfile = "3" rand = "0.8" + +[[example]] +name = "qr_seed_encode" +required-features = ["qr"] diff --git a/crates/rvf/rvf-runtime/src/agi_container.rs b/crates/rvf/rvf-runtime/src/agi_container.rs new file mode 100644 index 00000000..ab88abbf --- /dev/null +++ b/crates/rvf/rvf-runtime/src/agi_container.rs @@ -0,0 +1,564 @@ +//! AGI Cognitive Container builder and validator (ADR-036). +//! +//! Assembles a complete intelligence runtime into a single RVF artifact: +//! micro Linux kernel, Claude Code + Claude Flow configs, world model, +//! evaluation harness, witness chains, tool adapters, and policies. + +use std::time::{SystemTime, UNIX_EPOCH}; + +use rvf_types::agi_container::*; + +use crate::seed_crypto; + +/// Builder for assembling an AGI cognitive container manifest. +/// +/// The manifest is a META segment in the RVF file. Other segments +/// (KERNEL_SEG, WASM_SEG, VEC_SEG, etc.) are added through the +/// main RVF write path; this builder only handles the manifest. +#[derive(Clone, Debug)] +pub struct AgiContainerBuilder { + /// Container UUID. + pub container_id: [u8; 16], + /// Build UUID. + pub build_id: [u8; 16], + /// Pinned model identifier string. + pub model_id: Option>, + /// Governance policy (binary). + pub policy: Option>, + /// Policy hash from ADR-035 GovernancePolicy. + pub policy_hash: [u8; 8], + /// Orchestrator config (Claude Code + Claude Flow). + pub orchestrator_config: Option>, + /// MCP tool adapter registry. + pub tool_registry: Option>, + /// Agent role prompts. + pub agent_prompts: Option>, + /// Evaluation task suite. + pub eval_tasks: Option>, + /// Grading rules. + pub eval_graders: Option>, + /// Skill library. + pub skill_library: Option>, + /// Replay script. + pub replay_script: Option>, + /// Kernel boot config. + pub kernel_config: Option>, + /// Network config. + pub network_config: Option>, + /// Coherence gate config. + pub coherence_config: Option>, + /// Project instructions (CLAUDE.md). + pub project_instructions: Option>, + /// Dependency snapshot. + pub dependency_snapshot: Option>, + /// Segment inventory. + pub segments: ContainerSegments, + /// Extra flags to OR in. + pub extra_flags: u16, +} + +impl AgiContainerBuilder { + /// Create a new builder with container and build IDs. + pub fn new(container_id: [u8; 16], build_id: [u8; 16]) -> Self { + Self { + container_id, + build_id, + model_id: None, + policy: None, + policy_hash: [0; 8], + orchestrator_config: None, + tool_registry: None, + agent_prompts: None, + eval_tasks: None, + eval_graders: None, + skill_library: None, + replay_script: None, + kernel_config: None, + network_config: None, + coherence_config: None, + project_instructions: None, + dependency_snapshot: None, + segments: ContainerSegments::default(), + extra_flags: 0, + } + } + + /// Pin the model to a specific version. + pub fn with_model_id(mut self, model_id: &str) -> Self { + self.model_id = Some(model_id.as_bytes().to_vec()); + self + } + + /// Set the governance policy. + pub fn with_policy(mut self, policy: &[u8], hash: [u8; 8]) -> Self { + self.policy = Some(policy.to_vec()); + self.policy_hash = hash; + self + } + + /// Set the Claude Code + Claude Flow orchestrator config. + pub fn with_orchestrator(mut self, config: &[u8]) -> Self { + self.orchestrator_config = Some(config.to_vec()); + self.extra_flags |= AGI_HAS_ORCHESTRATOR; + self + } + + /// Set the MCP tool adapter registry. + pub fn with_tool_registry(mut self, registry: &[u8]) -> Self { + self.tool_registry = Some(registry.to_vec()); + self.extra_flags |= AGI_HAS_TOOLS; + self + } + + /// Set agent role prompts. + pub fn with_agent_prompts(mut self, prompts: &[u8]) -> Self { + self.agent_prompts = Some(prompts.to_vec()); + self + } + + /// Set the evaluation task suite. + pub fn with_eval_tasks(mut self, tasks: &[u8]) -> Self { + self.eval_tasks = Some(tasks.to_vec()); + self.extra_flags |= AGI_HAS_EVAL; + self + } + + /// Set the grading rules. + pub fn with_eval_graders(mut self, graders: &[u8]) -> Self { + self.eval_graders = Some(graders.to_vec()); + self + } + + /// Set the promoted skill library. + pub fn with_skill_library(mut self, skills: &[u8]) -> Self { + self.skill_library = Some(skills.to_vec()); + self.extra_flags |= AGI_HAS_SKILLS; + self + } + + /// Set the replay automation script. + pub fn with_replay_script(mut self, script: &[u8]) -> Self { + self.replay_script = Some(script.to_vec()); + self.extra_flags |= AGI_REPLAY_CAPABLE; + self + } + + /// Set the kernel boot configuration. + pub fn with_kernel_config(mut self, config: &[u8]) -> Self { + self.kernel_config = Some(config.to_vec()); + self + } + + /// Set the network configuration. + pub fn with_network_config(mut self, config: &[u8]) -> Self { + self.network_config = Some(config.to_vec()); + self + } + + /// Set the coherence gate configuration. + pub fn with_coherence_config(mut self, config: &[u8]) -> Self { + self.coherence_config = Some(config.to_vec()); + self.extra_flags |= AGI_HAS_COHERENCE_GATES; + self + } + + /// Set the project instructions (CLAUDE.md content). + pub fn with_project_instructions(mut self, instructions: &[u8]) -> Self { + self.project_instructions = Some(instructions.to_vec()); + self + } + + /// Set the dependency snapshot. + pub fn with_dependency_snapshot(mut self, snapshot: &[u8]) -> Self { + self.dependency_snapshot = Some(snapshot.to_vec()); + self + } + + /// Mark offline capability. + pub fn offline_capable(mut self) -> Self { + self.extra_flags |= AGI_OFFLINE_CAPABLE; + self + } + + /// Declare the segment inventory. + pub fn with_segments(mut self, segments: ContainerSegments) -> Self { + self.segments = segments; + self + } + + /// Build the manifest TLV payload (sections only, no header). + fn build_sections(&self) -> Vec { + let mut payload = Vec::new(); + + let mut write_section = |tag: u16, data: &[u8]| { + payload.extend_from_slice(&tag.to_le_bytes()); + payload.extend_from_slice(&(data.len() as u32).to_le_bytes()); + payload.extend_from_slice(data); + }; + + write_section(AGI_TAG_CONTAINER_ID, &self.container_id); + write_section(AGI_TAG_BUILD_ID, &self.build_id); + + if let Some(ref mid) = self.model_id { + write_section(AGI_TAG_MODEL_ID, mid); + } + if let Some(ref p) = self.policy { + write_section(AGI_TAG_POLICY, p); + } + if let Some(ref oc) = self.orchestrator_config { + write_section(AGI_TAG_ORCHESTRATOR, oc); + } + if let Some(ref tr) = self.tool_registry { + write_section(AGI_TAG_TOOL_REGISTRY, tr); + } + if let Some(ref ap) = self.agent_prompts { + write_section(AGI_TAG_AGENT_PROMPTS, ap); + } + if let Some(ref et) = self.eval_tasks { + write_section(AGI_TAG_EVAL_TASKS, et); + } + if let Some(ref eg) = self.eval_graders { + write_section(AGI_TAG_EVAL_GRADERS, eg); + } + if let Some(ref sl) = self.skill_library { + write_section(AGI_TAG_SKILL_LIBRARY, sl); + } + if let Some(ref rs) = self.replay_script { + write_section(AGI_TAG_REPLAY_SCRIPT, rs); + } + if let Some(ref kc) = self.kernel_config { + write_section(AGI_TAG_KERNEL_CONFIG, kc); + } + if let Some(ref nc) = self.network_config { + write_section(AGI_TAG_NETWORK_CONFIG, nc); + } + if let Some(ref cc) = self.coherence_config { + write_section(AGI_TAG_COHERENCE_CONFIG, cc); + } + if let Some(ref pi) = self.project_instructions { + write_section(AGI_TAG_PROJECT_INSTRUCTIONS, pi); + } + if let Some(ref ds) = self.dependency_snapshot { + write_section(AGI_TAG_DEPENDENCY_SNAPSHOT, ds); + } + + payload + } + + /// Build the manifest: header + TLV sections. + pub fn build(self) -> Result<(Vec, AgiContainerHeader), ContainerError> { + let sections = self.build_sections(); + + let model_id_hash = match &self.model_id { + Some(mid) => seed_crypto::seed_content_hash(mid), + None => [0u8; 8], + }; + + let flags = self.segments.to_flags() | self.extra_flags; + + let created_ns = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_nanos() as u64; + + let header = AgiContainerHeader { + magic: AGI_MAGIC, + version: 1, + flags, + container_id: self.container_id, + build_id: self.build_id, + created_ns, + model_id_hash, + policy_hash: self.policy_hash, + }; + + let mut payload = Vec::with_capacity(AGI_HEADER_SIZE + sections.len()); + payload.extend_from_slice(&header.to_bytes()); + payload.extend_from_slice(§ions); + + // Mark manifest as present for validation. + let mut segs = self.segments.clone(); + segs.manifest_present = true; + + Ok((payload, header)) + } + + /// Build and sign with HMAC-SHA256. + pub fn build_and_sign( + mut self, + key: &[u8], + ) -> Result<(Vec, AgiContainerHeader), ContainerError> { + self.segments.crypto_present = true; + let (unsigned, mut header) = self.build()?; + header.flags |= AGI_SIGNED; + + let sig = seed_crypto::sign_seed(key, &unsigned); + let mut signed = unsigned; + // Re-write the header with SIGNED flag. + let header_bytes = header.to_bytes(); + signed[..AGI_HEADER_SIZE].copy_from_slice(&header_bytes); + signed.extend_from_slice(&sig); + + Ok((signed, header)) + } +} + +/// Parsed AGI container manifest with zero-copy section references. +#[derive(Debug)] +pub struct ParsedAgiManifest<'a> { + /// Parsed header. + pub header: AgiContainerHeader, + /// Model identifier string. + pub model_id: Option<&'a [u8]>, + /// Policy bytes. + pub policy: Option<&'a [u8]>, + /// Orchestrator config. + pub orchestrator_config: Option<&'a [u8]>, + /// Tool registry. + pub tool_registry: Option<&'a [u8]>, + /// Agent prompts. + pub agent_prompts: Option<&'a [u8]>, + /// Eval tasks. + pub eval_tasks: Option<&'a [u8]>, + /// Eval graders. + pub eval_graders: Option<&'a [u8]>, + /// Skill library. + pub skill_library: Option<&'a [u8]>, + /// Replay script. + pub replay_script: Option<&'a [u8]>, + /// Kernel config. + pub kernel_config: Option<&'a [u8]>, + /// Network config. + pub network_config: Option<&'a [u8]>, + /// Coherence gate config. + pub coherence_config: Option<&'a [u8]>, + /// Project instructions. + pub project_instructions: Option<&'a [u8]>, + /// Dependency snapshot. + pub dependency_snapshot: Option<&'a [u8]>, +} + +impl<'a> ParsedAgiManifest<'a> { + /// Parse a manifest from bytes. + pub fn parse(data: &'a [u8]) -> Result { + let header = AgiContainerHeader::from_bytes(data) + .map_err(|_| ContainerError::InvalidConfig("invalid header"))?; + + let mut result = Self { + header, + model_id: None, + policy: None, + orchestrator_config: None, + tool_registry: None, + agent_prompts: None, + eval_tasks: None, + eval_graders: None, + skill_library: None, + replay_script: None, + kernel_config: None, + network_config: None, + coherence_config: None, + project_instructions: None, + dependency_snapshot: None, + }; + + // Parse TLV sections after header. + let mut pos = AGI_HEADER_SIZE; + while pos + 6 <= data.len() { + let tag = u16::from_le_bytes([data[pos], data[pos + 1]]); + let length = u32::from_le_bytes([ + data[pos + 2], data[pos + 3], data[pos + 4], data[pos + 5], + ]) as usize; + pos += 6; + + if pos + length > data.len() { + break; + } + + let value = &data[pos..pos + length]; + match tag { + AGI_TAG_MODEL_ID => result.model_id = Some(value), + AGI_TAG_POLICY => result.policy = Some(value), + AGI_TAG_ORCHESTRATOR => result.orchestrator_config = Some(value), + AGI_TAG_TOOL_REGISTRY => result.tool_registry = Some(value), + AGI_TAG_AGENT_PROMPTS => result.agent_prompts = Some(value), + AGI_TAG_EVAL_TASKS => result.eval_tasks = Some(value), + AGI_TAG_EVAL_GRADERS => result.eval_graders = Some(value), + AGI_TAG_SKILL_LIBRARY => result.skill_library = Some(value), + AGI_TAG_REPLAY_SCRIPT => result.replay_script = Some(value), + AGI_TAG_KERNEL_CONFIG => result.kernel_config = Some(value), + AGI_TAG_NETWORK_CONFIG => result.network_config = Some(value), + AGI_TAG_COHERENCE_CONFIG => result.coherence_config = Some(value), + AGI_TAG_PROJECT_INSTRUCTIONS => result.project_instructions = Some(value), + AGI_TAG_DEPENDENCY_SNAPSHOT => result.dependency_snapshot = Some(value), + _ => {} // forward-compat: ignore unknown tags + } + + pos += length; + } + + Ok(result) + } + + /// Get the model ID as a UTF-8 string. + pub fn model_id_str(&self) -> Option<&str> { + self.model_id.and_then(|b| core::str::from_utf8(b).ok()) + } + + /// Check if the manifest has all sections needed for autonomous operation. + pub fn is_autonomous_capable(&self) -> bool { + self.orchestrator_config.is_some() + && self.eval_tasks.is_some() + && self.eval_graders.is_some() + } +} + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +#[cfg(test)] +mod tests { + use super::*; + + const ORCHESTRATOR_CONFIG: &[u8] = br#"{ + "claude_code": { + "model": "claude-opus-4-6", + "max_turns": 100, + "permission_mode": "bypassPermissions" + }, + "claude_flow": { + "topology": "hierarchical", + "max_agents": 15, + "strategy": "specialized", + "memory": "hybrid" + } + }"#; + + const TOOL_REGISTRY: &[u8] = br#"[ + {"name": "ruvector_query", "type": "vector_search"}, + {"name": "ruvector_cypher", "type": "graph_query"}, + {"name": "ruvector_commit_delta", "type": "write"}, + {"name": "rvf_snapshot", "type": "snapshot"}, + {"name": "rvf_witness_export", "type": "export"}, + {"name": "eval_run", "type": "evaluation"} + ]"#; + + const COHERENCE_CONFIG: &[u8] = br#"{ + "min_cut_threshold": 0.7, + "contradiction_pressure_max": 0.3, + "quarantine_ttl_hours": 24, + "skill_promotion_k": 5, + "rollback_on_violation": true + }"#; + + #[test] + fn build_full_container() { + let segs = ContainerSegments { + kernel_present: true, + kernel_size: 5_000_000, + wasm_count: 2, + wasm_total_size: 60_000, + vec_segment_count: 4, + index_segment_count: 2, + witness_count: 100, + crypto_present: false, + manifest_present: false, + total_size: 0, + }; + + let builder = AgiContainerBuilder::new([0x01; 16], [0x02; 16]) + .with_model_id("claude-opus-4-6") + .with_policy(b"autonomous", [0xAA; 8]) + .with_orchestrator(ORCHESTRATOR_CONFIG) + .with_tool_registry(TOOL_REGISTRY) + .with_agent_prompts(b"You are a coder agent...") + .with_eval_tasks(b"[{\"id\":1,\"spec\":\"fix bug\"}]") + .with_eval_graders(b"[{\"type\":\"test_pass\"}]") + .with_skill_library(b"[]") + .with_replay_script(b"#!/bin/sh\nrvf replay $1") + .with_kernel_config(b"console=ttyS0 root=/dev/vda") + .with_network_config(b"{\"port\":8080}") + .with_coherence_config(COHERENCE_CONFIG) + .with_project_instructions(b"# CLAUDE.md\nFollow DDD...") + .with_dependency_snapshot(b"sha256:abc123") + .offline_capable() + .with_segments(segs); + + let (payload, header) = builder.build().unwrap(); + + assert!(header.is_valid_magic()); + assert!(header.has_kernel()); + assert!(header.has_orchestrator()); + assert!(header.is_replay_capable()); + assert!(header.is_offline_capable()); + + // Parse it back. + let parsed = ParsedAgiManifest::parse(&payload).unwrap(); + assert_eq!(parsed.model_id_str(), Some("claude-opus-4-6")); + assert_eq!(parsed.orchestrator_config.unwrap(), ORCHESTRATOR_CONFIG); + assert_eq!(parsed.tool_registry.unwrap(), TOOL_REGISTRY); + assert_eq!(parsed.coherence_config.unwrap(), COHERENCE_CONFIG); + assert!(parsed.project_instructions.is_some()); + assert!(parsed.is_autonomous_capable()); + } + + #[test] + fn signed_container_round_trip() { + let key = b"container-signing-key-for-tests!"; + let builder = AgiContainerBuilder::new([0x10; 16], [0x20; 16]) + .with_model_id("claude-opus-4-6") + .with_orchestrator(ORCHESTRATOR_CONFIG) + .with_eval_tasks(b"[]") + .with_eval_graders(b"[]") + .with_segments(ContainerSegments { + kernel_present: true, + manifest_present: false, + ..Default::default() + }); + + let (payload, header) = builder.build_and_sign(key).unwrap(); + assert!(header.is_signed()); + + // Verify signature. + let unsigned_len = payload.len() - 32; + let sig = &payload[unsigned_len..]; + assert!(seed_crypto::verify_seed(key, &payload[..unsigned_len], sig)); + } + + #[test] + fn minimal_container() { + let builder = AgiContainerBuilder::new([0x30; 16], [0x40; 16]) + .with_segments(ContainerSegments { + kernel_present: true, + manifest_present: true, + ..Default::default() + }); + + let (payload, header) = builder.build().unwrap(); + assert!(header.has_kernel()); + + let parsed = ParsedAgiManifest::parse(&payload).unwrap(); + assert!(parsed.model_id.is_none()); + assert!(!parsed.is_autonomous_capable()); + } + + #[test] + fn segment_validation() { + // Live mode needs kernel or WASM. + let segs = ContainerSegments { + manifest_present: true, + kernel_present: true, + ..Default::default() + }; + assert!(segs.validate(ExecutionMode::Live).is_ok()); + + // Replay mode needs witness. + let segs2 = ContainerSegments { + manifest_present: true, + witness_count: 50, + ..Default::default() + }; + assert!(segs2.validate(ExecutionMode::Replay).is_ok()); + } +} diff --git a/crates/rvf/rvf-runtime/src/lib.rs b/crates/rvf/rvf-runtime/src/lib.rs index 23a01526..5f7bd529 100644 --- a/crates/rvf/rvf-runtime/src/lib.rs +++ b/crates/rvf/rvf-runtime/src/lib.rs @@ -35,6 +35,7 @@ pub mod status; pub mod store; pub mod witness; pub mod write_path; +pub mod agi_container; pub use adversarial::{ adaptive_n_probe, centroid_distance_cv, combined_effective_n_probe, @@ -73,3 +74,6 @@ pub use store::RvfStore; pub use witness::{ GovernancePolicy, ParsedWitness, ScorecardBuilder, WitnessBuilder, WitnessError, }; +pub use agi_container::{ + AgiContainerBuilder, ParsedAgiManifest, +}; diff --git a/crates/rvf/rvf-types/src/agi_container.rs b/crates/rvf/rvf-types/src/agi_container.rs new file mode 100644 index 00000000..4b247c74 --- /dev/null +++ b/crates/rvf/rvf-types/src/agi_container.rs @@ -0,0 +1,480 @@ +//! AGI Cognitive Container types (ADR-036). +//! +//! An AGI container is a single RVF file that packages the complete intelligence +//! runtime: micro Linux kernel, Claude Code orchestrator config, Claude Flow +//! swarm manager, RuVector world model, evaluation harness, witness chains, +//! and tool adapters. +//! +//! Wire format: 64-byte `AgiContainerHeader` + TLV manifest sections. +//! The header is stored as a META segment (SegmentType::Meta) in the RVF file, +//! alongside the KERNEL_SEG, WASM_SEG, VEC_SEG, INDEX_SEG, WITNESS_SEG, and +//! CRYPTO_SEG that hold the actual payload data. + +/// Magic bytes for AGI container manifest: "RVAG" (RuVector AGI). +pub const AGI_MAGIC: u32 = 0x5256_4147; + +/// Size of the AGI container header in bytes. +pub const AGI_HEADER_SIZE: usize = 64; + +// --- Flags --- + +/// Container includes a KERNEL_SEG with micro Linux kernel. +pub const AGI_HAS_KERNEL: u16 = 1 << 0; +/// Container includes WASM_SEG modules. +pub const AGI_HAS_WASM: u16 = 1 << 1; +/// Container includes Claude Code + Claude Flow orchestrator config. +pub const AGI_HAS_ORCHESTRATOR: u16 = 1 << 2; +/// Container includes VEC_SEG + INDEX_SEG world model data. +pub const AGI_HAS_WORLD_MODEL: u16 = 1 << 3; +/// Container includes evaluation harness (task suite + graders). +pub const AGI_HAS_EVAL: u16 = 1 << 4; +/// Container includes promoted skill library. +pub const AGI_HAS_SKILLS: u16 = 1 << 5; +/// Container includes ADR-035 witness chain. +pub const AGI_HAS_WITNESS: u16 = 1 << 6; +/// Container is cryptographically signed (HMAC-SHA256 or Ed25519). +pub const AGI_SIGNED: u16 = 1 << 7; +/// All tool outputs stored — container supports replay mode. +pub const AGI_REPLAY_CAPABLE: u16 = 1 << 8; +/// Container can run without network (offline-first). +pub const AGI_OFFLINE_CAPABLE: u16 = 1 << 9; +/// Container includes MCP tool adapter registry. +pub const AGI_HAS_TOOLS: u16 = 1 << 10; +/// Container includes coherence gate configuration. +pub const AGI_HAS_COHERENCE_GATES: u16 = 1 << 11; + +// --- TLV tags for the manifest payload --- + +/// Container UUID. +pub const AGI_TAG_CONTAINER_ID: u16 = 0x0100; +/// Build UUID. +pub const AGI_TAG_BUILD_ID: u16 = 0x0101; +/// Pinned model identifier (UTF-8 string, e.g. "claude-opus-4-6"). +pub const AGI_TAG_MODEL_ID: u16 = 0x0102; +/// Serialized governance policy (binary, per ADR-035). +pub const AGI_TAG_POLICY: u16 = 0x0103; +/// Claude Code + Claude Flow orchestrator config (JSON or TOML). +pub const AGI_TAG_ORCHESTRATOR: u16 = 0x0104; +/// MCP tool adapter registry (JSON array of tool schemas). +pub const AGI_TAG_TOOL_REGISTRY: u16 = 0x0105; +/// Agent role prompts (one per agent type). +pub const AGI_TAG_AGENT_PROMPTS: u16 = 0x0106; +/// Evaluation task suite (JSON array of task specs). +pub const AGI_TAG_EVAL_TASKS: u16 = 0x0107; +/// Grading rules (JSON or binary grader config). +pub const AGI_TAG_EVAL_GRADERS: u16 = 0x0108; +/// Promoted skill library (serialized skill nodes). +pub const AGI_TAG_SKILL_LIBRARY: u16 = 0x0109; +/// Replay automation script. +pub const AGI_TAG_REPLAY_SCRIPT: u16 = 0x010A; +/// Kernel boot parameters (command line, initrd config). +pub const AGI_TAG_KERNEL_CONFIG: u16 = 0x010B; +/// Network configuration (ports, endpoints, TLS). +pub const AGI_TAG_NETWORK_CONFIG: u16 = 0x010C; +/// Coherence gate thresholds and rules. +pub const AGI_TAG_COHERENCE_CONFIG: u16 = 0x010D; +/// Claude.md project instructions. +pub const AGI_TAG_PROJECT_INSTRUCTIONS: u16 = 0x010E; +/// Dependency snapshot hashes (pinned repos, packages). +pub const AGI_TAG_DEPENDENCY_SNAPSHOT: u16 = 0x010F; + +// --- Execution mode --- + +/// Container execution mode. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[repr(u8)] +pub enum ExecutionMode { + /// Replay: no external tool calls, use stored receipts. + /// All graders must match exactly. Witness chain must match. + Replay = 0, + /// Verify: live tool calls, outputs stored and hashed. + /// Outputs must pass same tests. Costs within expected bounds. + Verify = 1, + /// Live: full autonomous operation with governance controls. + Live = 2, +} + +impl TryFrom for ExecutionMode { + type Error = u8; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(Self::Replay), + 1 => Ok(Self::Verify), + 2 => Ok(Self::Live), + other => Err(other), + } + } +} + +/// Wire-format AGI container header (exactly 64 bytes, `repr(C)`). +/// +/// ```text +/// Offset Type Field +/// 0x00 u32 magic (0x52564147 "RVAG") +/// 0x04 u16 version +/// 0x06 u16 flags +/// 0x08 [u8; 16] container_id (UUID) +/// 0x18 [u8; 16] build_id (UUID) +/// 0x28 u64 created_ns (UNIX epoch nanoseconds) +/// 0x30 [u8; 8] model_id_hash (SHA-256 truncated) +/// 0x38 [u8; 8] policy_hash (SHA-256 truncated) +/// ``` +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(C)] +pub struct AgiContainerHeader { + /// Magic bytes: AGI_MAGIC. + pub magic: u32, + /// Format version (currently 1). + pub version: u16, + /// Bitfield flags indicating which segments are present. + pub flags: u16, + /// Unique container identifier (UUID). + pub container_id: [u8; 16], + /// Build identifier (UUID, changes on each repackaging). + pub build_id: [u8; 16], + /// Creation timestamp (nanoseconds since UNIX epoch). + pub created_ns: u64, + /// SHA-256 of the pinned model identifier, truncated to 8 bytes. + pub model_id_hash: [u8; 8], + /// SHA-256 of the governance policy, truncated to 8 bytes. + pub policy_hash: [u8; 8], +} + +// Compile-time size assertion. +const _: () = assert!(core::mem::size_of::() == 64); + +impl AgiContainerHeader { + /// Check magic bytes. + pub const fn is_valid_magic(&self) -> bool { + self.magic == AGI_MAGIC + } + + /// Check if the container is signed. + pub const fn is_signed(&self) -> bool { + self.flags & AGI_SIGNED != 0 + } + + /// Check if the container has a micro Linux kernel. + pub const fn has_kernel(&self) -> bool { + self.flags & AGI_HAS_KERNEL != 0 + } + + /// Check if the container has an orchestrator config. + pub const fn has_orchestrator(&self) -> bool { + self.flags & AGI_HAS_ORCHESTRATOR != 0 + } + + /// Check if the container supports replay mode. + pub const fn is_replay_capable(&self) -> bool { + self.flags & AGI_REPLAY_CAPABLE != 0 + } + + /// Check if the container can run offline. + pub const fn is_offline_capable(&self) -> bool { + self.flags & AGI_OFFLINE_CAPABLE != 0 + } + + /// Serialize header to a 64-byte array. + pub fn to_bytes(&self) -> [u8; AGI_HEADER_SIZE] { + let mut buf = [0u8; AGI_HEADER_SIZE]; + buf[0..4].copy_from_slice(&self.magic.to_le_bytes()); + buf[4..6].copy_from_slice(&self.version.to_le_bytes()); + buf[6..8].copy_from_slice(&self.flags.to_le_bytes()); + buf[8..24].copy_from_slice(&self.container_id); + buf[24..40].copy_from_slice(&self.build_id); + buf[40..48].copy_from_slice(&self.created_ns.to_le_bytes()); + buf[48..56].copy_from_slice(&self.model_id_hash); + buf[56..64].copy_from_slice(&self.policy_hash); + buf + } + + /// Deserialize header from a byte slice (>= 64 bytes). + pub fn from_bytes(data: &[u8]) -> Result { + if data.len() < AGI_HEADER_SIZE { + return Err(crate::RvfError::SizeMismatch { + expected: AGI_HEADER_SIZE, + got: data.len(), + }); + } + let magic = u32::from_le_bytes([data[0], data[1], data[2], data[3]]); + if magic != AGI_MAGIC { + return Err(crate::RvfError::BadMagic { + expected: AGI_MAGIC, + got: magic, + }); + } + let mut container_id = [0u8; 16]; + container_id.copy_from_slice(&data[8..24]); + let mut build_id = [0u8; 16]; + build_id.copy_from_slice(&data[24..40]); + let mut model_id_hash = [0u8; 8]; + model_id_hash.copy_from_slice(&data[48..56]); + let mut policy_hash = [0u8; 8]; + policy_hash.copy_from_slice(&data[56..64]); + + Ok(Self { + magic, + version: u16::from_le_bytes([data[4], data[5]]), + flags: u16::from_le_bytes([data[6], data[7]]), + container_id, + build_id, + created_ns: u64::from_le_bytes([ + data[40], data[41], data[42], data[43], + data[44], data[45], data[46], data[47], + ]), + model_id_hash, + policy_hash, + }) + } +} + +/// Required segments for a valid AGI container. +/// +/// Used by the container builder/validator to ensure completeness. +#[derive(Clone, Debug, Default)] +pub struct ContainerSegments { + /// KERNEL_SEG: micro Linux kernel (e.g. Firecracker-compatible vmlinux). + pub kernel_present: bool, + /// KERNEL_SEG size in bytes. + pub kernel_size: u64, + /// WASM_SEG: interpreter + microkernel modules. + pub wasm_count: u16, + /// Total WASM_SEG size in bytes. + pub wasm_total_size: u64, + /// VEC_SEG: world model vector count. + pub vec_segment_count: u16, + /// INDEX_SEG: HNSW index count. + pub index_segment_count: u16, + /// WITNESS_SEG: witness bundle count. + pub witness_count: u32, + /// CRYPTO_SEG: present. + pub crypto_present: bool, + /// META segment with AGI manifest: present. + pub manifest_present: bool, + /// Total container size in bytes. + pub total_size: u64, +} + +impl ContainerSegments { + /// Validate that the container has all required segments for a given + /// execution mode. + pub fn validate(&self, mode: ExecutionMode) -> Result<(), ContainerError> { + // All modes require the manifest. + if !self.manifest_present { + return Err(ContainerError::MissingSegment("AGI manifest")); + } + + match mode { + ExecutionMode::Replay => { + // Replay needs witness chains. + if self.witness_count == 0 { + return Err(ContainerError::MissingSegment("witness chain")); + } + } + ExecutionMode::Verify | ExecutionMode::Live => { + // Verify/Live need at least kernel or WASM. + if !self.kernel_present && self.wasm_count == 0 { + return Err(ContainerError::MissingSegment( + "kernel or WASM runtime", + )); + } + } + } + + Ok(()) + } + + /// Compute the flags bitfield from present segments. + pub fn to_flags(&self) -> u16 { + let mut flags: u16 = 0; + if self.kernel_present { + flags |= AGI_HAS_KERNEL; + } + if self.wasm_count > 0 { + flags |= AGI_HAS_WASM; + } + if self.witness_count > 0 { + flags |= AGI_HAS_WITNESS; + } + if self.crypto_present { + flags |= AGI_SIGNED; + } + flags + } +} + +/// Error type for AGI container operations. +#[derive(Debug, PartialEq, Eq)] +pub enum ContainerError { + /// A required segment is missing. + MissingSegment(&'static str), + /// Container exceeds size limit. + TooLarge { size: u64 }, + /// Invalid segment configuration. + InvalidConfig(&'static str), + /// Signature verification failed. + SignatureInvalid, +} + +impl core::fmt::Display for ContainerError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + ContainerError::MissingSegment(s) => write!(f, "missing segment: {s}"), + ContainerError::TooLarge { size } => write!(f, "container too large: {size} bytes"), + ContainerError::InvalidConfig(s) => write!(f, "invalid config: {s}"), + ContainerError::SignatureInvalid => write!(f, "signature verification failed"), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn agi_header_size() { + assert_eq!(core::mem::size_of::(), 64); + } + + #[test] + fn agi_header_round_trip() { + let hdr = AgiContainerHeader { + magic: AGI_MAGIC, + version: 1, + flags: AGI_HAS_KERNEL | AGI_HAS_ORCHESTRATOR | AGI_HAS_WORLD_MODEL + | AGI_HAS_EVAL | AGI_SIGNED | AGI_REPLAY_CAPABLE, + container_id: [0x42; 16], + build_id: [0x43; 16], + created_ns: 1_700_000_000_000_000_000, + model_id_hash: [0xAA; 8], + policy_hash: [0xBB; 8], + }; + let bytes = hdr.to_bytes(); + assert_eq!(bytes.len(), AGI_HEADER_SIZE); + let decoded = AgiContainerHeader::from_bytes(&bytes).unwrap(); + assert_eq!(decoded, hdr); + } + + #[test] + fn agi_header_bad_magic() { + let mut bytes = [0u8; 64]; + bytes[0..4].copy_from_slice(&0xDEADBEEFu32.to_le_bytes()); + assert!(AgiContainerHeader::from_bytes(&bytes).is_err()); + } + + #[test] + fn agi_header_too_short() { + assert!(AgiContainerHeader::from_bytes(&[0u8; 32]).is_err()); + } + + #[test] + fn agi_flags() { + let hdr = AgiContainerHeader { + magic: AGI_MAGIC, + version: 1, + flags: AGI_HAS_KERNEL | AGI_HAS_ORCHESTRATOR | AGI_SIGNED, + container_id: [0; 16], + build_id: [0; 16], + created_ns: 0, + model_id_hash: [0; 8], + policy_hash: [0; 8], + }; + assert!(hdr.has_kernel()); + assert!(hdr.has_orchestrator()); + assert!(hdr.is_signed()); + assert!(!hdr.is_replay_capable()); + assert!(!hdr.is_offline_capable()); + } + + #[test] + fn execution_mode_round_trip() { + for raw in 0..=2u8 { + let m = ExecutionMode::try_from(raw).unwrap(); + assert_eq!(m as u8, raw); + } + assert!(ExecutionMode::try_from(3).is_err()); + } + + #[test] + fn segments_validate_replay_needs_witness() { + let segs = ContainerSegments { + manifest_present: true, + witness_count: 0, + ..Default::default() + }; + assert_eq!( + segs.validate(ExecutionMode::Replay), + Err(ContainerError::MissingSegment("witness chain")) + ); + } + + #[test] + fn segments_validate_live_needs_runtime() { + let segs = ContainerSegments { + manifest_present: true, + kernel_present: false, + wasm_count: 0, + ..Default::default() + }; + assert_eq!( + segs.validate(ExecutionMode::Live), + Err(ContainerError::MissingSegment("kernel or WASM runtime")) + ); + } + + #[test] + fn segments_validate_live_with_kernel() { + let segs = ContainerSegments { + manifest_present: true, + kernel_present: true, + ..Default::default() + }; + assert!(segs.validate(ExecutionMode::Live).is_ok()); + } + + #[test] + fn segments_validate_live_with_wasm() { + let segs = ContainerSegments { + manifest_present: true, + wasm_count: 2, + ..Default::default() + }; + assert!(segs.validate(ExecutionMode::Live).is_ok()); + } + + #[test] + fn segments_validate_replay_with_witness() { + let segs = ContainerSegments { + manifest_present: true, + witness_count: 10, + ..Default::default() + }; + assert!(segs.validate(ExecutionMode::Replay).is_ok()); + } + + #[test] + fn segments_to_flags() { + let segs = ContainerSegments { + kernel_present: true, + wasm_count: 1, + witness_count: 5, + crypto_present: true, + ..Default::default() + }; + let flags = segs.to_flags(); + assert_ne!(flags & AGI_HAS_KERNEL, 0); + assert_ne!(flags & AGI_HAS_WASM, 0); + assert_ne!(flags & AGI_HAS_WITNESS, 0); + assert_ne!(flags & AGI_SIGNED, 0); + } + + #[test] + fn container_error_display() { + let e = ContainerError::MissingSegment("kernel"); + assert!(format!("{e}").contains("kernel")); + let e2 = ContainerError::TooLarge { size: 999 }; + assert!(format!("{e2}").contains("999")); + } +} diff --git a/crates/rvf/rvf-types/src/lib.rs b/crates/rvf/rvf-types/src/lib.rs index deac9b19..a372c574 100644 --- a/crates/rvf/rvf-types/src/lib.rs +++ b/crates/rvf/rvf-types/src/lib.rs @@ -44,6 +44,7 @@ pub mod sha256; pub mod ed25519; pub mod wasm_bootstrap; pub mod witness; +pub mod agi_container; pub use attestation::{AttestationHeader, AttestationWitnessType, TeePlatform, KEY_TYPE_TEE_BOUND}; pub use ebpf::{ @@ -118,3 +119,10 @@ pub use wasm_bootstrap::{ WASM_FEAT_REFERENCE_TYPES, WASM_FEAT_THREADS, WASM_FEAT_TAIL_CALL, WASM_FEAT_GC, WASM_FEAT_EXCEPTION_HANDLING, }; +pub use agi_container::{ + AgiContainerHeader, ContainerSegments, ContainerError, ExecutionMode, + AGI_MAGIC, AGI_HEADER_SIZE, + AGI_HAS_KERNEL, AGI_HAS_WASM, AGI_HAS_ORCHESTRATOR, AGI_HAS_WORLD_MODEL, + AGI_HAS_EVAL, AGI_HAS_SKILLS, AGI_HAS_WITNESS, AGI_SIGNED, + AGI_REPLAY_CAPABLE, AGI_OFFLINE_CAPABLE, AGI_HAS_TOOLS, AGI_HAS_COHERENCE_GATES, +};