use chio_core::capability::CapabilityToken;
use chio_core::credit::CreditBondRow;
use chio_core::receipt::{ChildRequestReceipt, ChioReceipt};
use crate::capability_lineage::CapabilitySnapshot;
use crate::checkpoint::KernelCheckpoint;
#[derive(Debug, Clone)]
pub struct RetentionConfig {
pub retention_days: u64,
pub max_size_bytes: u64,
pub archive_path: String,
pub tenant_id: Option<String>,
}
impl Default for RetentionConfig {
fn default() -> Self {
Self {
retention_days: 90,
max_size_bytes: 10_737_418_240,
archive_path: "receipts-archive.sqlite3".to_string(),
tenant_id: None,
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum ReceiptStoreError {
#[error("sqlite error: {0}")]
Sqlite(#[from] rusqlite::Error),
#[error("sqlite pool error: {0}")]
Pool(String),
#[error("serialization error: {0}")]
Json(#[from] serde_json::Error),
#[error("failed to prepare receipt store directory: {0}")]
Io(#[from] std::io::Error),
#[error("crypto decode error: {0}")]
CryptoDecode(String),
#[error("canonical json error: {0}")]
Canonical(String),
#[error("invalid outcome filter: {0}")]
InvalidOutcome(String),
#[error("conflict: {0}")]
Conflict(String),
#[error("not found: {0}")]
NotFound(String),
}
pub trait ReceiptStore: Send {
fn append_chio_receipt(&mut self, receipt: &ChioReceipt) -> Result<(), ReceiptStoreError>;
fn append_chio_receipt_returning_seq(
&mut self,
receipt: &ChioReceipt,
) -> Result<Option<u64>, ReceiptStoreError> {
self.append_chio_receipt(receipt)?;
Ok(None)
}
fn append_child_receipt(
&mut self,
receipt: &ChildRequestReceipt,
) -> Result<(), ReceiptStoreError>;
fn receipts_canonical_bytes_range(
&self,
_start_seq: u64,
_end_seq: u64,
) -> Result<Vec<(u64, Vec<u8>)>, ReceiptStoreError> {
Ok(Vec::new())
}
fn store_checkpoint(
&mut self,
_checkpoint: &KernelCheckpoint,
) -> Result<(), ReceiptStoreError> {
Ok(())
}
fn load_checkpoint_by_seq(
&self,
_checkpoint_seq: u64,
) -> Result<Option<KernelCheckpoint>, ReceiptStoreError> {
Ok(None)
}
fn supports_kernel_signed_checkpoints(&self) -> bool {
false
}
fn record_capability_snapshot(
&mut self,
_token: &CapabilityToken,
_parent_capability_id: Option<&str>,
) -> Result<(), ReceiptStoreError> {
Ok(())
}
fn get_capability_snapshot(
&self,
_capability_id: &str,
) -> Result<Option<CapabilitySnapshot>, ReceiptStoreError> {
Ok(None)
}
fn get_capability_delegation_chain(
&self,
_capability_id: &str,
) -> Result<Vec<CapabilitySnapshot>, ReceiptStoreError> {
Ok(Vec::new())
}
fn resolve_credit_bond(
&self,
_bond_id: &str,
) -> Result<Option<CreditBondRow>, ReceiptStoreError> {
Ok(None)
}
fn record_session_anchor(
&mut self,
_session_id: &str,
_anchor_id: &str,
_auth_context_fingerprint: &str,
_issued_at: u64,
_supersedes_anchor_id: Option<&str>,
_anchor_json: &serde_json::Value,
) -> Result<(), ReceiptStoreError> {
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn record_request_lineage(
&mut self,
_session_id: &str,
_request_id: &str,
_parent_request_id: Option<&str>,
_session_anchor_id: Option<&str>,
_recorded_at: u64,
_request_fingerprint: Option<&str>,
_lineage_json: &serde_json::Value,
) -> Result<(), ReceiptStoreError> {
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn record_receipt_lineage_statement(
&mut self,
_child_receipt_id: &str,
_request_id: Option<&str>,
_session_id: Option<&str>,
_session_anchor_id: Option<&str>,
_parent_request_id: Option<&str>,
_parent_receipt_id: Option<&str>,
_chain_id: Option<&str>,
_recorded_at: u64,
_statement_json: &serde_json::Value,
) -> Result<(), ReceiptStoreError> {
Ok(())
}
fn get_receipt_lineage_verification(
&self,
_receipt_id: &str,
) -> Result<Option<ReceiptLineageVerification>, ReceiptStoreError> {
Ok(None)
}
fn list_receipt_lineage_statement_links(
&self,
_receipt_id: &str,
) -> Result<Vec<ReceiptLineageStatementLink>, ReceiptStoreError> {
Ok(Vec::new())
}
fn as_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
None
}
}
#[derive(Debug, Clone)]
pub struct StoredToolReceipt {
pub seq: u64,
pub receipt: ChioReceipt,
}
#[derive(Debug, Clone)]
pub struct StoredChildReceipt {
pub seq: u64,
pub receipt: ChildRequestReceipt,
}
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ReceiptLineageVerification {
pub receipt_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub request_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session_anchor_id: Option<String>,
pub session_anchor_verified: bool,
pub parent_request_verified: bool,
pub parent_receipt_verified: bool,
pub replay_protected: bool,
}
impl ReceiptLineageVerification {
#[must_use]
pub fn delegated_call_chain_bound(&self) -> bool {
self.parent_receipt_verified
|| (self.session_anchor_verified && self.parent_request_verified)
}
}
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ReceiptLineageStatementLink {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub statement_id: Option<String>,
pub child_receipt_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub child_request_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parent_receipt_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parent_request_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session_anchor_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chain_id: Option<String>,
pub recorded_at: u64,
}
#[derive(Debug, Clone)]
pub struct FederatedEvidenceShareImport {
pub share_id: String,
pub manifest_hash: String,
pub exported_at: u64,
pub issuer: String,
pub partner: String,
pub signer_public_key: String,
pub require_proofs: bool,
pub query_json: String,
pub tool_receipts: Vec<StoredToolReceipt>,
pub capability_lineage: Vec<CapabilitySnapshot>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct FederatedEvidenceShareSummary {
pub share_id: String,
pub manifest_hash: String,
pub imported_at: u64,
pub exported_at: u64,
pub issuer: String,
pub partner: String,
pub signer_public_key: String,
pub require_proofs: bool,
pub tool_receipts: u64,
pub capability_lineage: u64,
}