44import logging
55import ssl
66from dataclasses import dataclass
7- from typing import Dict , Optional , Union
7+ from typing import Dict , Optional , Protocol , Union
88
99from x402 import x402Client
1010from x402 .http .clients import x402HttpxClient
@@ -34,36 +34,80 @@ def metadata(self) -> Dict:
3434 )
3535
3636
37- class TEEConnection :
38- """Maintains a verified connection to a single TEE endpoint.
37+ class TEEConnectionInterface ( Protocol ) :
38+ """Interface for TEE connection implementations."""
3939
40- Handles initial resolution from the on-chain registry (or an explicit URL),
41- TLS certificate pinning, background health checks, and automatic failover
42- when the current TEE becomes unavailable.
40+ def get (self ) -> ActiveTEE : ...
41+ def ensure_refresh_loop (self ) -> None : ...
42+ async def reconnect (self ) -> None : ...
43+ async def close (self ) -> None : ...
4344
44- Use ``get()`` to obtain the current ``ActiveTEE`` snapshot for making requests.
45+
46+ class StaticTEEConnection :
47+ """TEE connection with a hardcoded endpoint URL.
48+
49+ No registry lookup, no background refresh. TLS certificate verification
50+ is disabled because self-hosted TEE servers typically use self-signed certs.
4551
4652 Args:
4753 x402_client: Configured x402 payment client for creating HTTP clients.
48- registry: TEERegistry for looking up active TEEs. None when using an explicit URL.
49- llm_server_url: Bypass the registry and connect directly to this URL.
54+ endpoint: The TEE endpoint URL to connect to.
5055 """
5156
52- def __init__ (
53- self ,
54- x402_client : x402Client ,
55- registry : Optional [TEERegistry ] = None ,
56- llm_server_url : Optional [str ] = None ,
57- ):
57+ def __init__ (self , x402_client : x402Client , endpoint : str ):
58+ self ._x402_client = x402_client
59+ self ._endpoint = endpoint
60+ self ._active : ActiveTEE = self ._connect ()
61+
62+ def get (self ) -> ActiveTEE :
63+ """Return a snapshot of the current TEE connection."""
64+ return self ._active
65+
66+ def _connect (self ) -> ActiveTEE :
67+ return ActiveTEE (
68+ endpoint = self ._endpoint ,
69+ http_client = x402HttpxClient (self ._x402_client , verify = False ),
70+ tee_id = None ,
71+ payment_address = None ,
72+ )
73+
74+ def ensure_refresh_loop (self ) -> None :
75+ """No-op — static connections don't refresh."""
76+ pass
77+
78+ async def reconnect (self ) -> None :
79+ """Rebuild the HTTP client (same endpoint)."""
80+ old_client = self ._active .http_client
81+ self ._active = self ._connect ()
82+ try :
83+ await old_client .aclose ()
84+ except Exception :
85+ logger .debug ("Failed to close previous HTTP client during reconnect." , exc_info = True )
86+
87+ async def close (self ) -> None :
88+ """Close the HTTP client."""
89+ await self ._active .http_client .aclose ()
90+
91+
92+ class RegistryTEEConnection :
93+ """TEE connection resolved from the on-chain registry.
94+
95+ Handles TLS certificate pinning, background health checks, and automatic
96+ failover when the current TEE becomes unavailable.
97+
98+ Args:
99+ x402_client: Configured x402 payment client for creating HTTP clients.
100+ registry: TEERegistry for looking up active TEEs.
101+ """
102+
103+ def __init__ (self , x402_client : x402Client , registry : TEERegistry ):
58104 self ._x402_client = x402_client
59105 self ._registry = registry
60- self ._llm_server_url = llm_server_url
61106
62- self ._active : Optional [ActiveTEE ] = None
63107 self ._refresh_lock = asyncio .Lock ()
64108 self ._refresh_task : Optional [asyncio .Task ] = None
65109
66- self ._connect ()
110+ self ._active : ActiveTEE = self . _connect ()
67111
68112 # ── Public API ──────────────────────────────────────────────────────
69113
@@ -73,28 +117,46 @@ def get(self) -> ActiveTEE:
73117
74118 # ── Connection management ───────────────────────────────────────────
75119
76- def _connect (self ) -> None :
120+ def _resolve_tee (self ):
121+ """Resolve TEE endpoint and metadata from the on-chain registry.
122+
123+ Returns:
124+ The TEE object from the registry.
125+
126+ Raises:
127+ RuntimeError: If the registry lookup fails.
128+ ValueError: If no active LLM proxy TEE is found.
129+ """
130+ try :
131+ tee = self ._registry .get_llm_tee ()
132+ except Exception as e :
133+ raise RuntimeError (f"Failed to fetch LLM TEE endpoint from registry: { e } " ) from e
134+
135+ if tee is None :
136+ raise ValueError ("No active LLM proxy TEE found in the registry. Pass llm_server_url explicitly to override." )
137+
138+ logger .info ("Using TEE endpoint from registry: %s (teeId=%s)" , tee .endpoint , tee .tee_id )
139+ return tee
140+
141+ def _connect (self ) -> ActiveTEE :
77142 """Resolve TEE from registry and create a secure HTTP client."""
78- endpoint , tls_cert_der , tee_id , payment_address = self ._resolve_tee (
79- self ._llm_server_url ,
80- self ._registry ,
81- )
143+ tee = self ._resolve_tee ()
82144
83- ssl_ctx = build_ssl_context_from_der (tls_cert_der ) if tls_cert_der else None
84- tls_verify : Union [ssl .SSLContext , bool ] = ssl_ctx if ssl_ctx else ( self . _llm_server_url is None )
145+ ssl_ctx = build_ssl_context_from_der (tee . tls_cert_der ) if tee . tls_cert_der else None
146+ tls_verify : Union [ssl .SSLContext , bool ] = ssl_ctx if ssl_ctx else True
85147
86- self . _active = ActiveTEE (
87- endpoint = endpoint ,
148+ return ActiveTEE (
149+ endpoint = tee . endpoint ,
88150 http_client = x402HttpxClient (self ._x402_client , verify = tls_verify ),
89- tee_id = tee_id ,
90- payment_address = payment_address ,
151+ tee_id = tee . tee_id ,
152+ payment_address = tee . payment_address ,
91153 )
92154
93155 async def reconnect (self ) -> None :
94156 """Connect to a new TEE from the registry and rebuild the HTTP client."""
95157 async with self ._refresh_lock :
96158 old_client = self ._active .http_client
97- self ._connect ()
159+ self ._active = self . _connect ()
98160 try :
99161 await old_client .aclose ()
100162 except Exception :
@@ -105,11 +167,8 @@ async def reconnect(self) -> None:
105167 def ensure_refresh_loop (self ) -> None :
106168 """Start the background TEE refresh loop if not already running.
107169
108- No-op when ``llm_server_url`` is set (bypasses the registry).
109170 Called lazily from async request methods since ``__init__`` is synchronous.
110171 """
111- if self ._llm_server_url is not None :
112- return
113172 if self ._refresh_task is not None and not self ._refresh_task .done ():
114173 return
115174 self ._refresh_task = asyncio .create_task (self ._tee_refresh_loop ())
@@ -139,34 +198,4 @@ async def close(self) -> None:
139198 if self ._refresh_task is not None :
140199 self ._refresh_task .cancel ()
141200 self ._refresh_task = None
142- if self ._active is not None :
143- await self ._active .http_client .aclose ()
144-
145- # ── Static helpers ──────────────────────────────────────────────────
146-
147- @staticmethod
148- def _resolve_tee (
149- tee_endpoint_override : Optional [str ],
150- registry : Optional [TEERegistry ],
151- ) -> tuple :
152- """Resolve TEE endpoint and metadata from the on-chain registry or explicit URL.
153-
154- Returns:
155- (endpoint, tls_cert_der, tee_id, payment_address)
156- """
157- if tee_endpoint_override is not None :
158- return tee_endpoint_override , None , None , None
159-
160- if registry is None :
161- raise ValueError ("Either llm_server_url or a TEERegistry instance must be provided." )
162-
163- try :
164- tee = registry .get_llm_tee ()
165- except Exception as e :
166- raise RuntimeError (f"Failed to fetch LLM TEE endpoint from registry: { e } " ) from e
167-
168- if tee is None :
169- raise ValueError ("No active LLM proxy TEE found in the registry. Pass llm_server_url explicitly to override." )
170-
171- logger .info ("Using TEE endpoint from registry: %s (teeId=%s)" , tee .endpoint , tee .tee_id )
172- return tee .endpoint , tee .tls_cert_der , tee .tee_id , tee .payment_address
201+ await self ._active .http_client .aclose ()
0 commit comments