use super::envelope::{HandoffEnvelope, HandoffProof};
use crate::types::{AgentId, TenantId};
use async_trait::async_trait;
use thiserror::Error;
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct HandoffState {
pub from_run: String,
pub merkle_root: String,
pub at_seq: Option<u64>,
pub redacted_state: Vec<u8>,
pub source_identity: AgentId,
pub tenant: Option<TenantId>,
}
impl HandoffState {
pub fn new(
from_run: impl Into<String>,
merkle_root: impl Into<String>,
redacted_state: Vec<u8>,
source_identity: AgentId,
tenant: Option<TenantId>,
at_seq: Option<u64>,
) -> Self {
Self {
from_run: from_run.into(),
merkle_root: merkle_root.into(),
at_seq,
redacted_state,
source_identity,
tenant,
}
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum HandoffError {
#[error("envelope expired at {expired_at}")]
Expired {
expired_at: String,
},
#[error("signature invalid: {0}")]
SignatureInvalid(String),
#[error("untrusted source identity: {0}")]
UntrustedSource(String),
#[error("serialisation: {0}")]
Serialisation(String),
#[error("storage unavailable: {0}")]
Storage(String),
#[error("internal: {0}")]
Internal(String),
#[error("unverifiable")]
Unverifiable {
reason: String,
},
}
#[async_trait]
pub trait Handoff: Send + Sync {
async fn package(
&self,
state: HandoffState,
signer: &dyn crate::signer::SignerProvider,
ttl: chrono::Duration,
) -> Result<HandoffEnvelope, HandoffError>;
async fn deliver(&self, envelope: HandoffEnvelope, to: AgentId)
-> Result<String, HandoffError>;
async fn verify(&self, envelope: &HandoffEnvelope) -> Result<HandoffProof, HandoffError>;
async fn verify_opaque(
&self,
envelope: &HandoffEnvelope,
) -> Result<HandoffProof, HandoffError> {
match self.verify(envelope).await {
Ok(proof) => Ok(proof),
Err(HandoffError::SignatureInvalid(_)) | Err(HandoffError::UntrustedSource(_)) => {
Err(HandoffError::Unverifiable {
reason: String::new(),
})
}
Err(other) => Err(other),
}
}
}