Skip to main content

chio_cross_protocol/
capability_bridge.rs

1use chio_core::capability::{scope::ChioScope, token::CapabilityToken};
2use chio_core::{canonical_json_bytes, sha256_hex};
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::discovery::DiscoveryProtocol;
7use crate::error::BridgeError;
8
9pub const CROSS_PROTOCOL_AUTHORITY_PATH: &str = "cross_protocol_orchestrator";
10pub const CROSS_PROTOCOL_CAPABILITY_ENVELOPE_SCHEMA: &str = "chio.cross-protocol-cap.v1";
11
12/// Stable capability reference carried across protocol boundaries.
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct CrossProtocolCapabilityRef {
16    pub chio_capability_id: String,
17    pub origin_protocol: DiscoveryProtocol,
18    #[serde(default, skip_serializing_if = "Option::is_none")]
19    pub protocol_context: Option<Value>,
20    pub parent_capability_hash: String,
21}
22
23impl CrossProtocolCapabilityRef {
24    pub fn from_capability(
25        capability: &CapabilityToken,
26        origin_protocol: DiscoveryProtocol,
27        protocol_context: Option<Value>,
28    ) -> Result<Self, BridgeError> {
29        Ok(Self {
30            chio_capability_id: capability.id.clone(),
31            origin_protocol,
32            protocol_context,
33            parent_capability_hash: parent_capability_hash(capability)?,
34        })
35    }
36}
37
38/// Attenuated envelope that records how a capability crossed a protocol hop.
39#[derive(Debug, Clone, Serialize, Deserialize)]
40#[serde(rename_all = "camelCase")]
41pub struct CrossProtocolCapabilityEnvelope {
42    pub schema: String,
43    pub capability_ref: CrossProtocolCapabilityRef,
44    pub target_protocol: DiscoveryProtocol,
45    pub attenuated_scope: ChioScope,
46    pub bridged_at: u64,
47    pub bridge_id: String,
48}
49
50/// One hop in a cross-protocol request trace.
51#[derive(Debug, Clone, Serialize, Deserialize)]
52#[serde(rename_all = "camelCase")]
53pub struct ProtocolHop {
54    pub protocol: DiscoveryProtocol,
55    pub request_id: String,
56    #[serde(default, skip_serializing_if = "Option::is_none")]
57    pub receipt_id: Option<String>,
58    pub bridge_id: String,
59    pub timestamp: u64,
60}
61
62/// Cross-hop request lineage.
63#[derive(Debug, Clone, Serialize, Deserialize)]
64#[serde(rename_all = "camelCase")]
65pub struct CrossProtocolTraceContext {
66    pub trace_id: String,
67    pub hops: Vec<ProtocolHop>,
68    pub session_fingerprint: String,
69}
70
71/// Protocol-specific capability extraction/injection hooks.
72pub trait CapabilityBridge: Send + Sync {
73    fn source_protocol(&self) -> DiscoveryProtocol;
74
75    fn extract_capability_ref(
76        &self,
77        request: &Value,
78    ) -> Result<Option<CrossProtocolCapabilityRef>, BridgeError>;
79
80    fn inject_capability_ref(
81        &self,
82        envelope: &mut Value,
83        cap_ref: &CrossProtocolCapabilityRef,
84    ) -> Result<(), BridgeError>;
85
86    fn protocol_context(&self, _request: &Value) -> Result<Option<Value>, BridgeError> {
87        Ok(None)
88    }
89}
90
91pub(crate) fn parent_capability_hash(capability: &CapabilityToken) -> Result<String, BridgeError> {
92    Ok(sha256_hex(&canonical_json_bytes(capability).map_err(
93        |error| BridgeError::Canonical(error.to_string()),
94    )?))
95}
96
97pub(crate) fn attenuate_scope_for_tool(
98    parent: &ChioScope,
99    server_id: &str,
100    tool_name: &str,
101) -> ChioScope {
102    let grants = parent
103        .grants
104        .iter()
105        .filter_map(|grant| {
106            let server_matches = grant.server_id == server_id || grant.server_id == "*";
107            let tool_matches = grant.tool_name == tool_name || grant.tool_name == "*";
108            if !server_matches || !tool_matches {
109                return None;
110            }
111            let mut narrowed = grant.clone();
112            narrowed.server_id = server_id.to_string();
113            narrowed.tool_name = tool_name.to_string();
114            Some(narrowed)
115        })
116        .collect();
117
118    ChioScope {
119        grants,
120        resource_grants: vec![],
121        prompt_grants: vec![],
122    }
123}