1use async_trait::async_trait;
2use sha2::Digest;
3use uuid::Uuid;
4use vex_core::audit::EvidenceCapsule;
5use vex_core::segment::semantic_commitment_hash;
6use vex_llm::Capability;
7
8#[async_trait]
13pub trait Gate: Send + Sync + std::fmt::Debug {
14 #[allow(clippy::too_many_arguments)]
16 async fn execute_gate(
17 &self,
18 agent_id: Uuid,
19 task_prompt: &str,
20 suggested_output: &str,
21 intent_data: Option<vex_core::segment::IntentData>,
22 confidence: f64,
23 capabilities: &[Capability],
24 nonce: Uuid,
25 ) -> EvidenceCapsule;
26
27 async fn verify_token(
29 &self,
30 token: &vex_core::ContinuationToken,
31 expected_aid: Option<&str>,
32 expected_intent_hash: Option<&str>,
33 expected_circuit_id: Option<&str>,
34 ) -> Result<bool, String>;
35}
36
37#[derive(Debug, Default)]
39pub struct GenericGateMock;
40
41#[async_trait]
42impl Gate for GenericGateMock {
43 #[allow(clippy::too_many_arguments)]
44 async fn execute_gate(
45 &self,
46 _agent_id: Uuid,
47 _task_prompt: &str,
48 suggested_output: &str,
49 _intent_data: Option<vex_core::segment::IntentData>,
50 confidence: f64,
51 capabilities: &[Capability],
52 nonce: Uuid,
53 ) -> EvidenceCapsule {
54 let (outcome, reason) = if confidence < 0.3 {
60 ("HALT", "LOW_CONFIDENCE")
61 } else if capabilities.contains(&Capability::Network)
62 && !suggested_output.to_lowercase().contains("http")
63 {
64 ("ALLOW", "SENSORS_ORANGE_NETWORK_IDLE")
66 } else if suggested_output.to_lowercase().contains("i'm sorry")
67 || suggested_output.to_lowercase().contains("cannot fulfill")
68 {
69 ("HALT", "REFUSAL_FILTER")
70 } else {
71 ("ALLOW", "SENSORS_GREEN")
72 };
73
74 EvidenceCapsule {
75 capsule_id: format!("mock-{}", &Uuid::new_v4().to_string()[..8]),
76 outcome: outcome.to_string(),
77 reason_code: reason.to_string(),
78 witness_receipt: "mock-receipt-0xdeadbeef".to_string(),
79 nonce: nonce.to_string(),
80 magpie_source: None,
81 gate_sensors: vex_core::segment::SchemaValue(serde_json::json!({
82 "confidence_sensor": if confidence > 0.5 { "GREEN" } else { "YELLOW" },
83 "content_length": suggested_output.len(),
84 })),
85 reproducibility_context: vex_core::segment::SchemaValue(serde_json::json!({
86 "gate_provider": "ChoraGateMock",
87 "version": "0.1.0",
88 })),
89 resolution_vep_hash: None,
90 escalation_id: None,
91 continuation_token: None,
92 intent_data: None,
93 vep_blob: None,
94 }
95 }
96
97 async fn verify_token(
98 &self,
99 _token: &vex_core::ContinuationToken,
100 _expected_aid: Option<&str>,
101 _expected_intent_hash: Option<&str>,
102 _expected_circuit_id: Option<&str>,
103 ) -> Result<bool, String> {
104 Ok(true)
106 }
107}
108
109#[derive(Debug, Clone)]
112pub struct HttpGate {
113 pub inner: std::sync::Arc<ChoraGate>,
114}
115
116impl HttpGate {
117 pub fn new(client: std::sync::Arc<dyn vex_chora::client::AuthorityClient>) -> Self {
118 let bridge = std::sync::Arc::new(vex_chora::AuthorityBridge::new(client));
119 Self {
120 inner: std::sync::Arc::new(ChoraGate {
121 bridge,
122 prover: None,
123 }),
124 }
125 }
126
127 pub fn with_prover(self, prover: std::sync::Arc<attest_rs::zk::AuditProver>) -> Self {
129 Self {
130 inner: std::sync::Arc::new(ChoraGate {
131 bridge: self.inner.bridge.clone(),
132 prover: Some(prover),
133 }),
134 }
135 }
136
137 pub fn with_identity(self, identity: std::sync::Arc<vex_hardware::api::AgentIdentity>) -> Self {
139 let bridge = (*self.inner.bridge).clone().with_identity(identity);
140 Self {
141 inner: std::sync::Arc::new(ChoraGate {
142 bridge: std::sync::Arc::new(bridge),
143 prover: self.inner.prover.clone(),
144 }),
145 }
146 }
147}
148
149#[async_trait]
150impl Gate for HttpGate {
151 #[allow(clippy::too_many_arguments)]
152 async fn execute_gate(
153 &self,
154 agent_id: Uuid,
155 task_prompt: &str,
156 suggested_output: &str,
157 intent_data: Option<vex_core::segment::IntentData>,
158 confidence: f64,
159 capabilities: &[Capability],
160 nonce: Uuid,
161 ) -> EvidenceCapsule {
162 self.inner
163 .execute_gate(
164 agent_id,
165 task_prompt,
166 suggested_output,
167 intent_data,
168 confidence,
169 capabilities,
170 nonce,
171 )
172 .await
173 }
174 async fn verify_token(
175 &self,
176 token: &vex_core::ContinuationToken,
177 expected_aid: Option<&str>,
178 expected_intent_hash: Option<&str>,
179 expected_circuit_id: Option<&str>,
180 ) -> Result<bool, String> {
181 self.inner
182 .verify_token(
183 token,
184 expected_aid,
185 expected_intent_hash,
186 expected_circuit_id,
187 )
188 .await
189 }
190}
191
192#[derive(Debug, Clone)]
194pub struct ChoraGate {
195 pub bridge: std::sync::Arc<vex_chora::AuthorityBridge>,
196 pub prover: Option<std::sync::Arc<attest_rs::zk::AuditProver>>,
197}
198
199impl ChoraGate {
200 pub fn generate_shadow_intent_salt(&self, agent_id: Uuid, nonce: Uuid) -> Vec<u8> {
204 let context = format!("shadow-intent-v1:{}:{}", agent_id, nonce);
205 if let Some(identity) = &self.bridge.identity {
206 let sig = identity.sign(context.as_bytes());
207 sha2::Sha256::digest(&sig).to_vec()
208 } else {
209 let mut hasher = sha2::Sha256::new();
211 hasher.update(b"VEX_FALLBACK_SEED_001");
212 hasher.update(context.as_bytes());
213 hasher.finalize().to_vec()
214 }
215 }
216}
217
218#[async_trait]
219impl Gate for ChoraGate {
220 #[allow(clippy::too_many_arguments)]
221 async fn execute_gate(
222 &self,
223 _agent_id: Uuid,
224 task_prompt: &str,
225 suggested_output: &str,
226 intent_data: Option<vex_core::segment::IntentData>,
227 confidence: f64,
228 capabilities: &[Capability],
229 nonce: Uuid,
230 ) -> EvidenceCapsule {
231 let intent = if let Some(id) = intent_data {
234 id
235 } else if let Some(_prover) = &self.prover {
236 let salt = self.generate_shadow_intent_salt(_agent_id, nonce);
237 let intent_hash = semantic_commitment_hash(task_prompt, &salt);
238
239 use sha2::Digest;
242 let preimage = sha2::Sha256::digest(vex_core::segment::canonical_encode_shadow_intent(
243 task_prompt,
244 &salt,
245 ));
246 let mut start_root_bytes = [0u8; 32];
247 start_root_bytes[0..8].copy_from_slice(&preimage[0..8]);
248 let start_root_hex = hex::encode(start_root_bytes);
249
250 let proof_blob = attest_rs::zk::AuditProver::prove_transition(
252 start_root_bytes,
253 hex::decode(&intent_hash)
254 .unwrap_or_default()
255 .try_into()
256 .unwrap_or([0u8; 32]),
257 )
258 .unwrap_or_default();
259
260 vex_core::segment::IntentData::Shadow {
261 commitment_hash: intent_hash.clone(),
262 stark_proof_b64: base64::Engine::encode(
263 &base64::engine::general_purpose::STANDARD,
264 &proof_blob,
265 ),
266 public_inputs: vex_core::segment::ShadowPublicInputs {
267 schema: vex_core::segment::ShadowPublicInputs::SCHEMA_V1.to_string(),
268 start_root: start_root_hex,
269 commitment_hash: intent_hash,
270 circuit_id: attest_rs::zk::AuditProver::CIRCUIT_ID.to_string(),
271 public_salt: hex::encode(salt),
272 verifier_params_hash: None,
273 }
274 .to_schema_value(),
275 circuit_id: Some(attest_rs::zk::AuditProver::CIRCUIT_ID.to_string()),
276 continuation_token: None,
277 metadata: vex_core::segment::SchemaValue::default(),
278 }
279 } else {
280 vex_core::segment::IntentData::Transparent {
281 request_sha256: hex::encode(sha2::Sha256::digest(suggested_output.as_bytes())),
282 confidence,
283 capabilities: capabilities.iter().map(|c| format!("{:?}", c)).collect(),
284 magpie_source: None,
285 continuation_token: None,
286 metadata: vex_core::segment::SchemaValue::default(),
287 }
288 };
289
290 match self
292 .bridge
293 .perform_handshake(intent.clone(), &nonce.to_string())
294 .await
295 {
296 Ok(capsule) => EvidenceCapsule {
297 capsule_id: capsule.capsule_id,
298 outcome: capsule.authority.outcome,
299 reason_code: capsule.authority.reason_code,
300 witness_receipt: capsule.witness.receipt_hash,
301 nonce: capsule.authority.nonce.clone(),
302 magpie_source: None,
303 gate_sensors: vex_core::segment::SchemaValue(serde_json::json!({
304 "trace_root": capsule.authority.trace_root,
305 "identity_type": capsule.identity.identity_type,
306 "agent_id": _agent_id.to_string(),
307 })),
308 reproducibility_context: vex_core::segment::SchemaValue(serde_json::json!({
309 "gate_provider": "ChoraGate",
310 "bridge_version": "v0.2.0",
311 })),
312 resolution_vep_hash: None,
313 escalation_id: capsule.authority.escalation_id,
314 continuation_token: capsule.authority.continuation_token,
315 intent_data: Some(intent),
316 vep_blob: None,
317 },
318 Err(e) => EvidenceCapsule {
319 capsule_id: "error".to_string(),
320 outcome: "HALT".to_string(),
321 reason_code: format!("CHORA_BRIDGE_ERROR: {}", e),
322 witness_receipt: "error-none".to_string(),
323 nonce: "0".to_string(),
324 magpie_source: None,
325 gate_sensors: vex_core::segment::SchemaValue(serde_json::Value::Null),
326 reproducibility_context: vex_core::segment::SchemaValue(serde_json::Value::Null),
327 resolution_vep_hash: None,
328 escalation_id: None,
329 continuation_token: None,
330 intent_data: None,
331 vep_blob: None,
332 },
333 }
334 }
335
336 async fn verify_token(
337 &self,
338 token: &vex_core::ContinuationToken,
339 expected_aid: Option<&str>,
340 expected_intent_hash: Option<&str>,
341 expected_circuit_id: Option<&str>,
342 ) -> Result<bool, String> {
343 self.bridge
344 .verify_continuation_token(
345 token,
346 expected_aid,
347 expected_intent_hash,
348 expected_circuit_id,
349 None,
350 None,
351 )
352 .await
353 }
354}
355
356pub mod titan;
357pub use titan::TitanGate;
358
359pub const SHADOW_INTENT_TEST_SALT: &[u8] = b"VEX_LAB_001";