qualia_client_core/wellfair/api/
accountability.rs1use sha2::{Digest, Sha256};
4
5use super::*;
6
7impl WebizenHostApi {
8 pub(crate) fn accountability_store(
19 &self,
20 ) -> Result<crate::accountability_store::AccountabilityStore, String> {
21 crate::accountability_store::AccountabilityStore::open(&self.storage_root)
22 .map_err(|e| e.to_string())
23 }
24
25 pub fn ledger_append(
27 &self,
28 kind: &str,
29 payload_json: &str,
30 ) -> Result<crate::accountability_ledger::LedgerEntry, String> {
31 self.accountability_store()?
32 .append_ledger(kind, payload_json, &self.signing_key, Self::now_unix())
33 .map_err(|e| e.to_string())
34 }
35
36 pub fn ledger_verify(
38 &self,
39 ) -> Result<Option<crate::accountability_ledger::LedgerTamper>, String> {
40 let verdict = self
41 .accountability_store()?
42 .verify_ledger()
43 .map_err(|e| e.to_string())?;
44 Ok(verdict.err())
45 }
46
47 pub fn ledger_entries(
49 &self,
50 limit: usize,
51 ) -> Result<Vec<crate::accountability_ledger::LedgerEntry>, String> {
52 self.accountability_store()?
53 .ledger_entries(limit)
54 .map_err(|e| e.to_string())
55 }
56
57 pub fn grant_consent_credential(
61 &self,
62 agent_did: &str,
63 scope: &str,
64 purpose: &str,
65 commitment_hex: &str,
66 wrapped_key_hex: &str,
67 expiry_unix: Option<u64>,
68 ) -> Result<crate::consent_credential::ConsentCredential, String> {
69 let commitment = crate::accountability_store::parse_commitment_hex(commitment_hex)?;
70 let wrapped_key =
71 hex::decode(wrapped_key_hex.trim()).map_err(|e| format!("wrapped key not hex: {e}"))?;
72 let now = Self::now_unix();
73 let id = {
74 let digest = Sha256::digest(format!("{agent_did}:{scope}:{now}").as_bytes());
75 format!("cc-{}", hex::encode(&digest[..6]))
76 };
77 let cred = crate::consent_credential::ConsentCredential::grant(
78 id,
79 &self.owner_did,
80 agent_did,
81 scope,
82 purpose,
83 commitment,
84 wrapped_key,
85 now,
86 expiry_unix,
87 );
88 self.accountability_store()?
89 .grant_credential(cred, &self.signing_key, now)
90 .map_err(|e| e.to_string())
91 }
92
93 pub fn revoke_consent_credential(&self, credential_id: &str) -> Result<bool, String> {
96 self.accountability_store()?
97 .revoke_credential(credential_id, &self.signing_key, Self::now_unix())
98 .map_err(|e| e.to_string())
99 }
100
101 pub fn list_consent_credentials(
103 &self,
104 ) -> Result<Vec<crate::consent_credential::ConsentCredential>, String> {
105 self.accountability_store()?
106 .list_credentials()
107 .map_err(|e| e.to_string())
108 }
109
110 pub fn record_conduct(
113 &self,
114 agent_did: &str,
115 credential_id: &str,
116 action: &str,
117 reason: &str,
118 commitment_hex: &str,
119 ) -> Result<crate::consent_credential::ConductRecord, String> {
120 let commitment = crate::accountability_store::parse_commitment_hex(commitment_hex)?;
121 self.accountability_store()?
122 .record_conduct(
123 agent_did,
124 credential_id,
125 action,
126 reason,
127 commitment,
128 &self.signing_key,
129 Self::now_unix(),
130 )
131 .map_err(|e| e.to_string())
132 }
133
134 pub fn conduct_audit_trail(
136 &self,
137 credential_id: &str,
138 ) -> Result<Vec<crate::consent_credential::ConductRecord>, String> {
139 self.accountability_store()?
140 .audit_trail(credential_id)
141 .map_err(|e| e.to_string())
142 }
143
144 pub fn record_guardian_notifications(
149 &self,
150 notifications: &[super::super::ingest_guardian::GuardianNotification],
151 ) -> Result<(), String> {
152 let store = self.accountability_store()?;
153 for n in notifications {
154 let payload = serde_json::to_string(n).map_err(|e| e.to_string())?;
155 store
156 .append_ledger(
157 "guardian_notified",
158 &payload,
159 &self.signing_key,
160 Self::now_unix(),
161 )
162 .map_err(|e| e.to_string())?;
163 }
164 Ok(())
165 }
166}