open_agent_profile/
canonical.rs1use sha2::{Digest, Sha256};
2use thiserror::Error;
3
4use crate::{AgentProfile, Digests, Document, object};
5
6#[derive(Debug, Error)]
8pub enum CanonicalError {
9 #[error("canonical JSON error: {0}")]
11 Serialize(#[from] serde_json::Error),
12}
13
14pub fn canonical_json(value: &impl serde::Serialize) -> Result<Vec<u8>, CanonicalError> {
16 Ok(serde_jcs::to_vec(value)?)
17}
18fn digest(value: &impl serde::Serialize) -> Result<String, CanonicalError> {
19 Ok(format!(
20 "sha256:{:x}",
21 Sha256::digest(canonical_json(value)?)
22 ))
23}
24pub fn profile_digest(profile: &AgentProfile) -> Result<String, CanonicalError> {
26 digest(profile)
27}
28pub fn spec_digest(profile: &AgentProfile) -> Result<String, CanonicalError> {
30 let mut metadata = object(profile.get("metadata")).clone();
31 for key in ["revision", "updated_at", "trust"] {
32 metadata.remove(key);
33 }
34 let mut identity = Document::new();
35 identity.insert("metadata".into(), metadata.into());
36 identity.insert(
37 "spec".into(),
38 profile.get("spec").cloned().unwrap_or_default(),
39 );
40 digest(&identity)
41}
42pub fn profile_digests(profile: &AgentProfile) -> Result<Digests, CanonicalError> {
44 Ok(Digests {
45 profile: profile_digest(profile)?,
46 spec: spec_digest(profile)?,
47 })
48}