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
This commit is contained in:
Claude 2026-02-15 18:47:56 +00:00
parent 4282461017
commit a88534bb80
5 changed files with 1060 additions and 0 deletions

View file

@ -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"]

View file

@ -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<Vec<u8>>,
/// Governance policy (binary).
pub policy: Option<Vec<u8>>,
/// Policy hash from ADR-035 GovernancePolicy.
pub policy_hash: [u8; 8],
/// Orchestrator config (Claude Code + Claude Flow).
pub orchestrator_config: Option<Vec<u8>>,
/// MCP tool adapter registry.
pub tool_registry: Option<Vec<u8>>,
/// Agent role prompts.
pub agent_prompts: Option<Vec<u8>>,
/// Evaluation task suite.
pub eval_tasks: Option<Vec<u8>>,
/// Grading rules.
pub eval_graders: Option<Vec<u8>>,
/// Skill library.
pub skill_library: Option<Vec<u8>>,
/// Replay script.
pub replay_script: Option<Vec<u8>>,
/// Kernel boot config.
pub kernel_config: Option<Vec<u8>>,
/// Network config.
pub network_config: Option<Vec<u8>>,
/// Coherence gate config.
pub coherence_config: Option<Vec<u8>>,
/// Project instructions (CLAUDE.md).
pub project_instructions: Option<Vec<u8>>,
/// Dependency snapshot.
pub dependency_snapshot: Option<Vec<u8>>,
/// 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<u8> {
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<u8>, 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(&sections);
// 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<u8>, 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<Self, ContainerError> {
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());
}
}

View file

@ -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,
};

View file

@ -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<u8> for ExecutionMode {
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
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::<AgiContainerHeader>() == 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<Self, crate::RvfError> {
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::<AgiContainerHeader>(), 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"));
}
}

View file

@ -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,
};