northroot-proof-engine 0.3.2

Core proof computation and validation library for PoSH (Proof of Shape) and PoX (Proof of Execution)
Documentation
//! v0.2 Proof type definitions
//!
//! This module defines the core types for the modular proof architecture:
//! - PoSH (Proof of Shape): Method transformation contracts
//! - PoX (Proof of Execution): Run evidence and commitments
//!
//! All types are designed to be deterministic, canonicalizable, and hashable.

use serde::{Deserialize, Serialize};

// ---------- Common Types ----------

/// SHA-256 hash in format: `sha256:<64 lowercase hex chars>`
pub type Sha256 = String;

/// ISO 8601 timestamp in RFC3339 format with millisecond precision (UTC)
/// Format: `YYYY-MM-DDTHH:MM:SS.sssZ`
pub type IsoTime = String;

/// URI string (e.g., `s3://bucket/path`, `https://example.com`)
pub type Uri = String;

/// Decentralized Identifier (e.g., `did:key:...`)
pub type Did = String;

/// Cryptographic signature envelope
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Signature {
    /// Algorithm identifier (e.g., "ed25519")
    pub alg: String,
    /// Key identifier (e.g., "did:key:...")
    pub kid: String,
    /// Base64URL-encoded signature bytes
    pub sig: String,
}

// ---------- PoSH (Proof of Shape) ----------

/// Span shape definition for computing span_shape_hash
///
/// Represents the operator contract: tool + version + parameter schema.
/// This is instance-agnostic and used for method shape computation.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SpanShape {
    /// Tool identifier (e.g., "io.read_csv")
    pub tool_id: String,
    /// Tool version (e.g., "1")
    pub tool_version: String,
    /// Parameter schema (types/defaults only; no values)
    /// This is a JSON object representing the parameter structure
    #[serde(rename = "param_schema")]
    pub param_schema: serde_json::Value,
    /// Optional checker reference
    #[serde(skip_serializing_if = "Option::is_none")]
    pub checker_ref: Option<String>,
}

/// Method Shape Manifest (PoSH)
///
/// Published once per method version. Proves the transformation contract
/// independent of any specific run or data values.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct MethodShapeManifest {
    /// Schema version tag (e.g., "method.v1")
    pub schema_version: String,
    /// Global method identifier (e.g., "com.acme/normalize-ledger")
    pub method_id: String,
    /// Semantic version (e.g., "1.0.0")
    pub version: String,
    /// Multiset root of span shapes: H(sorted(span_shape_hash_i, "|"))
    pub method_shape_root: Sha256,
    /// Optional DAG hash (nodes + edges)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub method_dag_hash: Option<Sha256>,
    /// Runtime pinning information
    pub runtime: RuntimePins,
    /// List of checker references
    #[serde(default)]
    pub checker_refs: Vec<String>,
    /// Default policy identifier
    #[serde(skip_serializing_if = "Option::is_none")]
    pub default_policy_id: Option<String>,
    /// Publisher signature over the manifest
    pub publisher_sig: Signature,
}

/// Runtime pinning information for reproducibility
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RuntimePins {
    /// OCI/WASM image digest
    pub image_digest: Sha256,
    /// Method fingerprint (e.g., "git:<commit>")
    pub method_fingerprint: String,
}

// ---------- PoX (Proof of Execution) ----------

/// Trace - Run context
///
/// Defines the execution context for a trace, including method reference,
/// data shape, policy, and routing information.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Trace {
    /// Schema version tag (e.g., "trace.v2")
    pub schema_version: String,
    /// Trace identifier
    pub trace_id: String,
    /// Method reference (links to PoSH)
    pub method_ref: MethodRef,
    /// Data shape hash (structure/sketches of input data)
    pub data_shape_hash: Sha256,
    /// Policy identifier
    pub policy_id: String,
    /// Identity route (DID)
    pub identity_route: Did,
    /// Content route (URI where artifacts are stored)
    pub content_route: Uri,
    /// Optional execution route (provider/region/runtime context)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub execution_route: Option<ExecutionRoute>,
}

/// Method reference linking to a registered method shape
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct MethodRef {
    /// Method identifier
    pub method_id: String,
    /// Method version
    pub version: String,
    /// Method shape root (from PoSH)
    pub method_shape_root: Sha256,
    /// Image digest
    pub image_digest: Sha256,
    /// Method fingerprint
    pub method_fingerprint: String,
}

/// Execution route information (optional)
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ExecutionRoute {
    /// Cloud provider (e.g., "aws", "gcp", "azure", "onprem")
    pub provider: String,
    /// Optional account identifier
    #[serde(skip_serializing_if = "Option::is_none")]
    pub account: Option<String>,
    /// Region identifier
    pub region: String,
    /// Runtime digest
    pub runtime_digest: Sha256,
}

/// Span Commitment - Atomic execution unit
///
/// This is the hashed commitment that proves a specific run occurred on
/// specific bytes at specific times. This is what gets hashed and signed.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SpanCommitment {
    /// Schema version tag (e.g., "proof.v2")
    pub schema_version: String,
    /// Trace identifier
    pub trace_id: String,
    /// Span identifier
    pub span_id: String,
    /// Method reference
    pub method_ref: MethodRef,
    /// Data shape hash
    pub data_shape_hash: Sha256,
    /// Span shape hash (from PoSH)
    pub span_shape_hash: Sha256,
    /// Input artifacts
    pub inputs: Vec<ArtifactRef>,
    /// Output artifacts
    pub outputs: Vec<ArtifactRef>,
    /// Start time (RFC3339 ms UTC)
    pub start_time: IsoTime,
    /// End time (RFC3339 ms UTC)
    pub end_time: IsoTime,
    /// Execution status
    pub status: SpanStatus,
    /// Optional metadata (causality hints, ordering)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub meta: Option<SpanMeta>,
}

/// Artifact reference (input or output)
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ArtifactRef {
    /// Artifact name
    pub name: String,
    /// Content hash (SHA-256 of artifact bytes)
    pub content_hash: Sha256,
    /// Optional URI where artifact can be retrieved
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uri: Option<Uri>,
}

/// Span execution status
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "UPPERCASE")]
pub enum SpanStatus {
    /// Execution completed successfully
    OK,
    /// Execution failed
    ERROR,
}

/// Optional span metadata (causality hints)
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SpanMeta {
    /// Optional Lamport timestamp
    #[serde(skip_serializing_if = "Option::is_none")]
    pub lamport: Option<u64>,
    /// Optional parent span IDs
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parents: Option<Vec<String>>,
    /// Optional order index
    #[serde(skip_serializing_if = "Option::is_none")]
    pub order_index: Option<u64>,
}

/// Signed Span - Transport envelope
///
/// This is the transport format for posting proofs. It contains the
/// commitment and an ed25519 signature over the canonicalized commitment.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SignedSpan {
    /// The span commitment (what gets hashed)
    pub commitment: SpanCommitment,
    /// ed25519 signature over sha256(canonical(commitment))
    pub signature: Signature,
}

/// Trace Roots - Rollup of span commitments
///
/// Computed at trace end. Contains the aggregated roots that prove
/// the entire trace execution.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct TraceRoots {
    /// Trace identifier
    pub trace_id: String,
    /// Hashes of canonical SpanCommitment for each span
    pub span_commitments: Vec<Sha256>,
    /// Trace set root: H(sorted(C_span_i, "|"))
    pub trace_set_root: Sha256,
    /// Optional identity root: H({trace_id, identity_route, sorted(C_span_i)})
    #[serde(skip_serializing_if = "Option::is_none")]
    pub identity_root: Option<Sha256>,
    /// Optional sequence root (order claim)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub trace_seq_root: Option<Sha256>,
    /// Method shape root (carried from method_ref)
    pub method_shape_root: Sha256,
    /// Optional Tier-0/1 verification status
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tier01_status: Option<String>,
    /// Optional server countersignature
    #[serde(skip_serializing_if = "Option::is_none")]
    pub server_sig: Option<Signature>,
}