1use crate::protocol::{
7 GuestRuntimeKind, MountDescriptor, ProjectedModuleDescriptor, RegisterHostCallbacksRequest,
8 SidecarRequestFrame, SidecarRequestPayload, SidecarResponseFrame, SidecarResponsePayload,
9 SignalHandlerRegistration, SoftwareDescriptor, WasmPermissionTier,
10};
11use crate::wire::DEFAULT_MAX_FRAME_BYTES;
12use rusqlite::Connection;
13use rustls::{ClientConnection, ServerConnection, StreamOwned};
14use secure_exec_bridge::{BridgeTypes, FilesystemSnapshot};
15use secure_exec_execution::{
16 v8_host::V8SessionHandle, JavascriptExecution, JavascriptSyncRpcRequest, PythonExecution,
17 PythonVfsRpcRequest, WasmExecution,
18};
19use secure_exec_kernel::kernel::{KernelProcessHandle, KernelVm};
20use secure_exec_kernel::mount_table::MountTable;
21use secure_exec_kernel::root_fs::RootFilesystemMode;
22use secure_exec_kernel::socket_table::SocketId;
23use secure_exec_sidecar_core::VmLayerStore;
24use secure_exec_vm_config as vm_config;
25use secure_exec_vm_config::PermissionsPolicy;
26use serde::{Deserialize, Serialize};
27use serde_json::Value;
28use std::collections::{BTreeMap, BTreeSet, VecDeque};
29use std::error::Error;
30use std::fmt;
31use std::fs::File;
32use std::net::{IpAddr, SocketAddr, TcpListener, TcpStream, UdpSocket};
33use std::os::unix::net::{UnixListener, UnixStream};
34use std::path::PathBuf;
35use std::sync::atomic::{AtomicBool, AtomicI64, Ordering};
36use std::sync::mpsc::{Receiver, Sender};
37use std::sync::{Arc, Condvar, Mutex};
38use std::time::{Duration, Instant};
39use tokio::sync::mpsc::UnboundedSender;
40
41pub(crate) type BridgeError<B> = <B as BridgeTypes>::Error;
46pub(crate) type SidecarKernel = KernelVm<MountTable>;
47pub(crate) type KernelSocketReadinessRegistry =
48 Arc<Mutex<BTreeMap<SocketId, KernelSocketReadinessTarget>>>;
49
50pub(crate) const EXECUTION_DRIVER_NAME: &str = "secure-exec-sidecar-execution";
55pub(crate) const JAVASCRIPT_COMMAND: &str = "node";
56pub(crate) const PYTHON_COMMAND: &str = "python";
57pub(crate) const WASM_COMMAND: &str = "wasm";
58pub(crate) const PYTHON_VFS_RPC_GUEST_ROOT: &str = "/";
63pub(crate) const EXECUTION_SANDBOX_ROOT_ENV: &str = "AGENTOS_SANDBOX_ROOT";
64pub(crate) const WASM_STDIO_SYNC_RPC_ENV: &str = "AGENTOS_WASI_STDIO_SYNC_RPC";
65#[cfg(test)]
66#[allow(dead_code)]
67pub(crate) const HOST_REALPATH_MAX_SYMLINK_DEPTH: usize = 40;
68pub(crate) const DISPOSE_VM_SIGTERM_GRACE: std::time::Duration =
69 std::time::Duration::from_millis(100);
70pub(crate) const DISPOSE_VM_SIGKILL_GRACE: std::time::Duration =
71 std::time::Duration::from_millis(100);
72pub(crate) const VM_DNS_SERVERS_METADATA_KEY: &str = "network.dns.servers";
73#[cfg(test)]
74#[allow(dead_code)]
75pub(crate) const VM_LISTEN_PORT_MIN_METADATA_KEY: &str = "network.listen.port_min";
76#[cfg(test)]
77#[allow(dead_code)]
78pub(crate) const VM_LISTEN_PORT_MAX_METADATA_KEY: &str = "network.listen.port_max";
79pub(crate) const VM_LISTEN_ALLOW_PRIVILEGED_METADATA_KEY: &str = "network.listen.allow_privileged";
80pub(crate) const DEFAULT_JAVASCRIPT_NET_BACKLOG: u32 = 511;
81pub(crate) const LOOPBACK_EXEMPT_PORTS_ENV: &str = "AGENTOS_LOOPBACK_EXEMPT_PORTS";
82pub(crate) const TOOL_DRIVER_NAME: &str = "secure-exec-host-callbacks";
83pub(crate) const MAPPED_HOST_FD_START: u32 = 1_000_000_000;
84
85#[derive(Debug, Clone)]
90pub struct NativeSidecarConfig {
91 pub sidecar_id: String,
92 pub max_frame_bytes: usize,
93 pub compile_cache_root: Option<PathBuf>,
94 pub expected_auth_token: Option<String>,
95 pub acp_termination_grace: Duration,
96}
97
98impl Default for NativeSidecarConfig {
99 fn default() -> Self {
100 Self {
101 sidecar_id: String::from("secure-exec-sidecar"),
102 max_frame_bytes: DEFAULT_MAX_FRAME_BYTES,
103 compile_cache_root: None,
104 expected_auth_token: None,
105 acp_termination_grace: Duration::from_secs(3),
106 }
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
111pub enum SidecarError {
112 InvalidState(String),
113 ProtocolVersionMismatch(String),
114 BridgeVersionMismatch(String),
115 Conflict(String),
116 Unauthorized(String),
117 Unsupported(String),
118 FrameTooLarge(String),
119 Kernel(String),
120 Plugin(String),
121 Execution(String),
122 Bridge(String),
123 Io(String),
124}
125
126impl fmt::Display for SidecarError {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 match self {
129 Self::InvalidState(message)
130 | Self::ProtocolVersionMismatch(message)
131 | Self::BridgeVersionMismatch(message)
132 | Self::Conflict(message)
133 | Self::Unauthorized(message)
134 | Self::Unsupported(message)
135 | Self::FrameTooLarge(message)
136 | Self::Kernel(message)
137 | Self::Plugin(message)
138 | Self::Execution(message)
139 | Self::Bridge(message)
140 | Self::Io(message) => f.write_str(message),
141 }
142 }
143}
144
145impl Error for SidecarError {}
146
147pub trait SidecarRequestTransport: Send + Sync {
148 fn send_request(
149 &self,
150 request: SidecarRequestFrame,
151 timeout: Duration,
152 ) -> Result<SidecarResponseFrame, SidecarError>;
153}
154
155#[derive(Clone)]
156pub(crate) struct SharedSidecarRequestClient {
157 transport: Option<Arc<dyn SidecarRequestTransport>>,
158 next_request_id: Arc<AtomicI64>,
159}
160
161impl Default for SharedSidecarRequestClient {
162 fn default() -> Self {
163 Self {
164 transport: None,
165 next_request_id: Arc::new(AtomicI64::new(-1)),
166 }
167 }
168}
169
170impl SharedSidecarRequestClient {
171 pub(crate) fn set_transport(&mut self, transport: Arc<dyn SidecarRequestTransport>) {
172 self.transport = Some(transport);
173 }
174
175 pub(crate) fn invoke(
176 &self,
177 ownership: crate::protocol::OwnershipScope,
178 payload: SidecarRequestPayload,
179 timeout: Duration,
180 ) -> Result<SidecarResponsePayload, SidecarError> {
181 let transport = self.transport.as_ref().ok_or_else(|| {
182 SidecarError::Unsupported(String::from("sidecar request transport is not configured"))
183 })?;
184 let request_id = self.next_request_id.fetch_sub(1, Ordering::Relaxed);
185 let request = SidecarRequestFrame::new(request_id, ownership.clone(), payload);
186 let response = transport.send_request(request, timeout)?;
187 if response.request_id != request_id {
188 return Err(SidecarError::InvalidState(format!(
189 "sidecar response {} did not match request {request_id}",
190 response.request_id
191 )));
192 }
193 if response.ownership != ownership {
194 return Err(SidecarError::InvalidState(String::from(
195 "sidecar response ownership did not match request ownership",
196 )));
197 }
198 Ok(response.payload)
199 }
200}
201
202pub trait EventSinkTransport: Send + Sync {
209 fn emit_event(&self, event: crate::wire::EventFrame) -> Result<(), SidecarError>;
210}
211
212#[derive(Clone, Default)]
213pub(crate) struct SharedEventSink {
214 transport: Option<Arc<dyn EventSinkTransport>>,
215}
216
217impl SharedEventSink {
218 pub(crate) fn set_transport(&mut self, transport: Arc<dyn EventSinkTransport>) {
219 self.transport = Some(transport);
220 }
221
222 pub(crate) fn try_emit(
228 &self,
229 event: crate::wire::EventFrame,
230 ) -> Result<Option<crate::wire::EventFrame>, SidecarError> {
231 match self.transport.as_ref() {
232 Some(transport) => {
233 transport.emit_event(event)?;
234 Ok(None)
235 }
236 None => Ok(Some(event)),
237 }
238 }
239}
240
241pub(crate) struct SharedBridge<B> {
246 pub(crate) inner: Arc<Mutex<B>>,
247 pub(crate) permissions: Arc<Mutex<BTreeMap<String, PermissionsPolicy>>>,
248 #[cfg(test)]
249 pub(crate) set_vm_permissions_outcomes: Arc<Mutex<VecDeque<Option<SidecarError>>>>,
250}
251
252impl<B> Clone for SharedBridge<B> {
253 fn clone(&self) -> Self {
254 Self {
255 inner: Arc::clone(&self.inner),
256 permissions: Arc::clone(&self.permissions),
257 #[cfg(test)]
258 set_vm_permissions_outcomes: Arc::clone(&self.set_vm_permissions_outcomes),
259 }
260 }
261}
262
263#[allow(dead_code)]
268#[derive(Debug)]
269pub(crate) struct ConnectionState {
270 pub(crate) auth_token: String,
271 pub(crate) sessions: BTreeSet<String>,
272}
273
274#[allow(dead_code)]
275#[derive(Debug)]
276pub(crate) struct SessionState {
277 pub(crate) connection_id: String,
278 pub(crate) placement: crate::protocol::SidecarPlacement,
279 pub(crate) metadata: BTreeMap<String, String>,
280 pub(crate) vm_ids: BTreeSet<String>,
281}
282
283#[allow(dead_code)]
284#[derive(Debug, Clone)]
285pub(crate) struct VmConfiguration {
286 pub(crate) mounts: Vec<MountDescriptor>,
287 pub(crate) software: Vec<SoftwareDescriptor>,
288 pub(crate) permissions: PermissionsPolicy,
289 pub(crate) module_access_cwd: Option<String>,
290 pub(crate) instructions: Vec<String>,
291 pub(crate) projected_modules: Vec<ProjectedModuleDescriptor>,
292 pub(crate) command_permissions: BTreeMap<String, WasmPermissionTier>,
293 pub(crate) js_runtime: Option<vm_config::JsRuntimeConfig>,
297 pub(crate) loopback_exempt_ports: Vec<u16>,
298}
299
300impl Default for VmConfiguration {
301 fn default() -> Self {
302 Self {
303 mounts: Vec::new(),
304 software: Vec::new(),
305 permissions: secure_exec_sidecar_core::permissions::deny_all_policy(),
306 module_access_cwd: None,
307 instructions: Vec::new(),
308 projected_modules: Vec::new(),
309 command_permissions: BTreeMap::new(),
310 js_runtime: None,
311 loopback_exempt_ports: Vec::new(),
312 }
313 }
314}
315
316#[allow(dead_code)]
317pub(crate) struct VmState {
318 pub(crate) connection_id: String,
319 pub(crate) session_id: String,
320 pub(crate) limits: crate::limits::VmLimits,
323 pub(crate) dns: VmDnsConfig,
324 pub(crate) listen_policy: VmListenPolicy,
325 pub(crate) create_loopback_exempt_ports: BTreeSet<u16>,
326 pub(crate) guest_env: BTreeMap<String, String>,
327 pub(crate) requested_runtime: GuestRuntimeKind,
328 pub(crate) root_filesystem_mode: RootFilesystemMode,
329 pub(crate) guest_cwd: String,
330 pub(crate) cwd: PathBuf,
331 pub(crate) host_cwd: PathBuf,
332 pub(crate) kernel: SidecarKernel,
333 pub(crate) kernel_socket_readiness: KernelSocketReadinessRegistry,
334 pub(crate) loaded_snapshot: Option<FilesystemSnapshot>,
335 pub(crate) configuration: VmConfiguration,
336 pub(crate) layers: VmLayerStore,
337 pub(crate) command_guest_paths: BTreeMap<String, String>,
338 pub(crate) command_permissions: BTreeMap<String, WasmPermissionTier>,
339 pub(crate) toolkits: BTreeMap<String, RegisterHostCallbacksRequest>,
340 pub(crate) active_processes: BTreeMap<String, ActiveProcess>,
341 pub(crate) exited_process_snapshots: VecDeque<ExitedProcessSnapshot>,
342 pub(crate) detached_child_processes: BTreeSet<String>,
343 pub(crate) signal_states: BTreeMap<String, BTreeMap<u32, SignalHandlerRegistration>>,
344 pub(crate) packages_staging_root: Option<PathBuf>,
348}
349
350#[derive(Debug, Clone)]
351pub(crate) struct ExitedProcessSnapshot {
352 pub(crate) captured_at: Instant,
353 pub(crate) process: crate::protocol::ProcessSnapshotEntry,
354}
355
356#[derive(Debug, Clone, Default)]
361pub(crate) struct VmDnsConfig {
362 pub(crate) name_servers: Vec<SocketAddr>,
363 pub(crate) overrides: BTreeMap<String, Vec<IpAddr>>,
364}
365
366#[derive(Debug, Clone)]
367pub(crate) struct JavascriptSocketPathContext {
368 pub(crate) sandbox_root: PathBuf,
369 pub(crate) mounts: Vec<MountDescriptor>,
370 pub(crate) listen_policy: VmListenPolicy,
371 pub(crate) loopback_exempt_ports: BTreeSet<u16>,
372 pub(crate) tcp_loopback_guest_to_host_ports: BTreeMap<(JavascriptSocketFamily, u16), u16>,
373 pub(crate) http_loopback_targets:
374 BTreeMap<(JavascriptSocketFamily, u16), JavascriptHttpLoopbackTarget>,
375 pub(crate) udp_loopback_guest_to_host_ports: BTreeMap<(JavascriptSocketFamily, u16), u16>,
376 pub(crate) udp_loopback_host_to_guest_ports: BTreeMap<(JavascriptSocketFamily, u16), u16>,
377 pub(crate) used_tcp_guest_ports: BTreeMap<JavascriptSocketFamily, BTreeSet<u16>>,
378 pub(crate) used_udp_guest_ports: BTreeMap<JavascriptSocketFamily, BTreeSet<u16>>,
379}
380
381#[derive(Debug, Clone)]
382pub(crate) struct JavascriptHttpLoopbackTarget {
383 pub(crate) process_id: String,
384 pub(crate) server_id: u64,
385}
386
387#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
388pub(crate) enum JavascriptSocketFamily {
389 Ipv4,
390 Ipv6,
391}
392
393impl JavascriptSocketFamily {
394 pub(crate) fn from_ip(ip: IpAddr) -> Self {
395 match ip {
396 IpAddr::V4(_) => Self::Ipv4,
397 IpAddr::V6(_) => Self::Ipv6,
398 }
399 }
400}
401
402impl From<JavascriptUdpFamily> for JavascriptSocketFamily {
403 fn from(value: JavascriptUdpFamily) -> Self {
404 match value {
405 JavascriptUdpFamily::Ipv4 => Self::Ipv4,
406 JavascriptUdpFamily::Ipv6 => Self::Ipv6,
407 }
408 }
409}
410
411#[derive(Debug, Clone, Copy)]
412pub(crate) struct VmListenPolicy {
413 pub(crate) port_min: u16,
414 pub(crate) port_max: u16,
415 pub(crate) allow_privileged: bool,
416}
417
418impl Default for VmListenPolicy {
419 fn default() -> Self {
420 Self {
421 port_min: 1,
422 port_max: u16::MAX,
423 allow_privileged: false,
424 }
425 }
426}
427
428#[allow(dead_code)]
433pub(crate) struct ActiveProcess {
434 pub(crate) kernel_pid: u32,
435 pub(crate) kernel_handle: KernelProcessHandle,
436 pub(crate) kernel_stdin_writer_fd: Option<u32>,
437 pub(crate) tty_master_fd: Option<u32>,
442 pub(crate) runtime: GuestRuntimeKind,
443 pub(crate) detached: bool,
444 pub(crate) execution: ActiveExecution,
445 pub(crate) guest_cwd: String,
446 pub(crate) env: BTreeMap<String, String>,
447 pub(crate) host_cwd: PathBuf,
448 pub(crate) host_write_dirty: bool,
449 pub(crate) mapped_host_fds: BTreeMap<u32, ActiveMappedHostFd>,
450 pub(crate) next_mapped_host_fd: u32,
451 pub(crate) pending_execution_events: VecDeque<ActiveExecutionEvent>,
452 pub(crate) pending_self_signal_exit: Option<i32>,
453 pub(crate) child_processes: BTreeMap<String, ActiveProcess>,
454 pub(crate) next_child_process_id: usize,
455 pub(crate) http_servers: BTreeMap<u64, ActiveHttpServer>,
456 pub(crate) pending_http_requests: BTreeMap<(u64, u64), Option<String>>,
457 pub(crate) http2: ActiveHttp2State,
458 pub(crate) tcp_listeners: BTreeMap<String, ActiveTcpListener>,
459 pub(crate) next_tcp_listener_id: usize,
460 pub(crate) tcp_sockets: BTreeMap<String, ActiveTcpSocket>,
461 pub(crate) next_tcp_socket_id: usize,
462 pub(crate) tcp_port_reservations: BTreeMap<String, (JavascriptSocketFamily, u16)>,
463 pub(crate) next_tcp_port_reservation_id: usize,
464 pub(crate) unix_listeners: BTreeMap<String, ActiveUnixListener>,
465 pub(crate) next_unix_listener_id: usize,
466 pub(crate) unix_sockets: BTreeMap<String, ActiveUnixSocket>,
467 pub(crate) next_unix_socket_id: usize,
468 pub(crate) udp_sockets: BTreeMap<String, ActiveUdpSocket>,
469 pub(crate) next_udp_socket_id: usize,
470 pub(crate) python_sockets: BTreeMap<u64, PythonHostSocket>,
474 pub(crate) next_python_socket_id: u64,
475 pub(crate) cipher_sessions: BTreeMap<u64, ActiveCipherSession>,
476 pub(crate) next_cipher_session_id: u64,
477 pub(crate) diffie_hellman_sessions: BTreeMap<u64, ActiveDiffieHellmanSession>,
478 pub(crate) next_diffie_hellman_session_id: u64,
479 pub(crate) sqlite_databases: BTreeMap<u64, ActiveSqliteDatabase>,
480 pub(crate) next_sqlite_database_id: u64,
481 pub(crate) sqlite_statements: BTreeMap<u64, ActiveSqliteStatement>,
482 pub(crate) next_sqlite_statement_id: u64,
483 pub(crate) tty_master_owner: Option<(u32, u32)>,
491 pub(crate) deferred_kernel_wait_rpc: Option<(JavascriptSyncRpcRequest, Instant)>,
496 pub(crate) module_resolution_cache: secure_exec_execution::LocalModuleResolutionCache,
503}
504
505pub(crate) struct ActiveMappedHostFd {
506 pub(crate) file: File,
507 pub(crate) path: PathBuf,
508}
509
510pub(crate) struct ActiveCipherSession {
511 pub(crate) context: crate::crypto_cipher::StreamCipherSession,
512}
513
514pub(crate) struct ActiveSqliteDatabase {
515 pub(crate) connection: Connection,
516 pub(crate) host_path: Option<PathBuf>,
517 pub(crate) vm_path: Option<String>,
518 pub(crate) dirty: bool,
519 pub(crate) transaction_depth: usize,
520 pub(crate) read_only: bool,
521}
522
523#[derive(Clone)]
524pub(crate) struct ActiveSqliteStatement {
525 pub(crate) database_id: u64,
526 pub(crate) sql: String,
527 pub(crate) return_arrays: bool,
528 pub(crate) read_bigints: bool,
529 pub(crate) allow_bare_named_parameters: bool,
530 pub(crate) allow_unknown_named_parameters: bool,
531}
532
533pub(crate) enum ActiveDiffieHellmanSession {
534 Dh(ActiveDhSession),
535 Ecdh(ActiveEcdhSession),
536}
537
538pub(crate) struct ActiveDhSession {
539 pub(crate) params: openssl::dh::Dh<openssl::pkey::Params>,
540 pub(crate) key_pair: Option<openssl::dh::Dh<openssl::pkey::Private>>,
541}
542
543pub(crate) struct ActiveEcdhSession {
544 pub(crate) curve: String,
545 pub(crate) key_pair: Option<openssl::ec::EcKey<openssl::pkey::Private>>,
546}
547
548#[derive(Debug, Clone, Copy, Default)]
549pub(crate) struct NetworkResourceCounts {
550 pub(crate) sockets: usize,
551 pub(crate) connections: usize,
552}
553
554#[derive(Debug)]
555pub(crate) struct ActiveHttpServer {
556 pub(crate) listener: TcpListener,
557 pub(crate) guest_local_addr: SocketAddr,
558 pub(crate) next_request_id: u64,
559}
560
561#[derive(Clone, Default)]
562pub(crate) struct ActiveHttp2State {
563 pub(crate) shared: Arc<Mutex<Http2SharedState>>,
564}
565
566#[derive(Default)]
567pub(crate) struct Http2SharedState {
568 pub(crate) next_session_id: u64,
569 pub(crate) next_stream_id: u64,
570 pub(crate) ready: Arc<Condvar>,
571 pub(crate) event_session: Option<V8SessionHandle>,
572 pub(crate) servers: BTreeMap<u64, ActiveHttp2Server>,
573 pub(crate) sessions: BTreeMap<u64, ActiveHttp2Session>,
574 pub(crate) streams: BTreeMap<u64, ActiveHttp2Stream>,
575 pub(crate) server_events: BTreeMap<u64, VecDeque<Http2BridgeEvent>>,
576 pub(crate) session_events: BTreeMap<u64, VecDeque<Http2BridgeEvent>>,
577}
578
579#[derive(Debug)]
580pub(crate) struct ActiveHttp2Server {
581 pub(crate) actual_local_addr: SocketAddr,
582 pub(crate) guest_local_addr: SocketAddr,
583 pub(crate) secure: bool,
584 pub(crate) tls: Option<JavascriptTlsBridgeOptions>,
585 pub(crate) closed: Arc<AtomicBool>,
586}
587
588#[derive(Debug, Clone)]
589pub(crate) struct ActiveHttp2Session {
590 pub(crate) command_tx: UnboundedSender<Http2SessionCommand>,
591}
592
593#[derive(Debug, Clone)]
594pub(crate) struct ActiveHttp2Stream {
595 pub(crate) session_id: u64,
596 pub(crate) paused: Arc<AtomicBool>,
597 pub(crate) resume_notify: Arc<tokio::sync::Notify>,
598}
599
600#[derive(Debug, Clone, Default, Serialize, Deserialize)]
601#[serde(default, rename_all = "camelCase")]
602pub(crate) struct Http2SocketSnapshot {
603 pub(crate) encrypted: bool,
604 pub(crate) allow_half_open: bool,
605 pub(crate) local_address: Option<String>,
606 pub(crate) local_port: Option<u16>,
607 pub(crate) local_family: Option<String>,
608 pub(crate) remote_address: Option<String>,
609 pub(crate) remote_port: Option<u16>,
610 pub(crate) remote_family: Option<String>,
611 pub(crate) servername: Option<String>,
612 pub(crate) alpn_protocol: Option<String>,
613}
614
615#[derive(Debug, Clone, Default, Serialize, Deserialize)]
616#[serde(default, rename_all = "camelCase")]
617pub(crate) struct Http2RuntimeSnapshot {
618 pub(crate) effective_local_window_size: u32,
619 pub(crate) local_window_size: u32,
620 pub(crate) remote_window_size: u32,
621 pub(crate) next_stream_id: u32,
622 pub(crate) outbound_queue_size: u32,
623 pub(crate) deflate_dynamic_table_size: u32,
624 pub(crate) inflate_dynamic_table_size: u32,
625}
626
627#[derive(Debug, Clone, Default, Serialize, Deserialize)]
628#[serde(default, rename_all = "camelCase")]
629pub(crate) struct Http2SessionSnapshot {
630 pub(crate) encrypted: bool,
631 pub(crate) alpn_protocol: Option<String>,
632 pub(crate) origin_set: Vec<String>,
633 pub(crate) local_settings: BTreeMap<String, Value>,
634 pub(crate) remote_settings: BTreeMap<String, Value>,
635 pub(crate) state: Http2RuntimeSnapshot,
636 pub(crate) socket: Http2SocketSnapshot,
637}
638
639#[derive(Debug, Clone, Default, Serialize, Deserialize)]
640#[serde(default, rename_all = "camelCase")]
641pub(crate) struct Http2BridgeEvent {
642 pub(crate) kind: String,
643 pub(crate) id: u64,
644 #[serde(skip_serializing_if = "Option::is_none")]
645 pub(crate) data: Option<String>,
646 #[serde(skip_serializing_if = "Option::is_none")]
647 pub(crate) extra: Option<String>,
648 #[serde(skip_serializing_if = "Option::is_none")]
649 pub(crate) extra_number: Option<u64>,
650 #[serde(skip_serializing_if = "Option::is_none")]
651 pub(crate) extra_headers: Option<String>,
652 #[serde(skip_serializing_if = "Option::is_none")]
653 pub(crate) flags: Option<u64>,
654}
655
656pub(crate) enum Http2SessionCommand {
657 Request {
658 headers_json: String,
659 options_json: String,
660 respond_to: Sender<Result<Value, String>>,
661 },
662 Settings {
663 settings_json: String,
664 respond_to: Sender<Result<Value, String>>,
665 },
666 SetLocalWindowSize {
667 size: u32,
668 respond_to: Sender<Result<Value, String>>,
669 },
670 Goaway {
671 error_code: u32,
672 last_stream_id: u32,
673 opaque_data: Option<Vec<u8>>,
674 respond_to: Sender<Result<Value, String>>,
675 },
676 Close {
677 abrupt: bool,
678 respond_to: Sender<Result<Value, String>>,
679 },
680 StreamRespond {
681 stream_id: u64,
682 headers_json: String,
683 respond_to: Sender<Result<Value, String>>,
684 },
685 StreamPush {
686 stream_id: u64,
687 headers_json: String,
688 respond_to: Sender<Result<Value, String>>,
689 },
690 StreamWrite {
691 stream_id: u64,
692 chunk: Vec<u8>,
693 end_stream: bool,
694 respond_to: Sender<Result<Value, String>>,
695 },
696 StreamClose {
697 stream_id: u64,
698 error_code: Option<u32>,
699 respond_to: Sender<Result<Value, String>>,
700 },
701 StreamRespondWithFile {
702 stream_id: u64,
703 body: Vec<u8>,
704 headers_json: String,
705 options_json: String,
706 respond_to: Sender<Result<Value, String>>,
707 },
708}
709
710#[derive(Debug)]
715pub(crate) enum JavascriptTcpListenerEvent {
716 Connection(PendingTcpSocket),
717 Error {
718 code: Option<String>,
719 message: String,
720 },
721}
722
723#[derive(Debug)]
724pub(crate) struct PendingTcpSocket {
725 pub(crate) stream: Option<TcpStream>,
726 pub(crate) kernel_socket_id: Option<SocketId>,
727 pub(crate) preallocated: bool,
728 pub(crate) guest_local_addr: SocketAddr,
729 pub(crate) guest_remote_addr: SocketAddr,
730}
731
732#[derive(Debug)]
733pub(crate) enum JavascriptTcpSocketEvent {
734 Data(Vec<u8>),
735 End,
736 Close {
737 had_error: bool,
738 },
739 Error {
740 code: Option<String>,
741 message: String,
742 },
743}
744
745#[derive(Clone, Debug)]
746pub(crate) struct JavascriptSocketEventPusher {
747 pub(crate) session: V8SessionHandle,
748 pub(crate) socket_id: String,
749}
750
751#[derive(Clone, Copy, Debug, PartialEq, Eq)]
752pub(crate) enum KernelSocketReadinessEvent {
753 Data,
754 Datagram,
755 Accept,
756}
757
758#[derive(Clone, Debug)]
759pub(crate) struct KernelSocketReadinessTarget {
760 pub(crate) session: V8SessionHandle,
761 pub(crate) target_id: String,
762 pub(crate) event: KernelSocketReadinessEvent,
763}
764
765#[derive(Debug)]
766pub(crate) struct ActiveTcpSocket {
767 pub(crate) stream: Option<Arc<Mutex<TcpStream>>>,
768 pub(crate) pending_read_stream: Option<Arc<Mutex<Option<TcpStream>>>>,
769 pub(crate) events: Option<Receiver<JavascriptTcpSocketEvent>>,
770 pub(crate) event_sender: Option<Sender<JavascriptTcpSocketEvent>>,
771 pub(crate) event_pusher: Arc<Mutex<Option<JavascriptSocketEventPusher>>>,
772 pub(crate) kernel_socket_id: Option<SocketId>,
773 pub(crate) no_delay: bool,
774 pub(crate) keep_alive: bool,
775 pub(crate) keep_alive_initial_delay_secs: Option<u64>,
776 pub(crate) guest_local_addr: SocketAddr,
777 pub(crate) guest_remote_addr: SocketAddr,
778 pub(crate) listener_id: Option<String>,
779 pub(crate) tls_mode: Arc<AtomicBool>,
780 pub(crate) tls_stream: Arc<Mutex<Option<ActiveTlsStream>>>,
781 pub(crate) tls_state: Arc<Mutex<Option<ActiveTlsState>>>,
782 pub(crate) loopback_tls_pending_write: Arc<Mutex<Option<LoopbackTlsPendingWriteHandle>>>,
783 pub(crate) saw_local_shutdown: Arc<AtomicBool>,
784 pub(crate) saw_remote_end: Arc<AtomicBool>,
785 pub(crate) close_notified: Arc<AtomicBool>,
786}
787
788#[derive(Debug)]
789pub(crate) struct LoopbackTlsTransportPair {
790 pub(crate) state: Mutex<LoopbackTlsTransportPairState>,
791 pub(crate) ready: Condvar,
792}
793
794#[derive(Debug, Default)]
795pub(crate) struct LoopbackTlsTransportPairState {
796 pub(crate) lower_to_higher: VecDeque<u8>,
797 pub(crate) higher_to_lower: VecDeque<u8>,
798 pub(crate) lower_write_closed: bool,
799 pub(crate) higher_write_closed: bool,
800 pub(crate) lower_closed: bool,
801 pub(crate) higher_closed: bool,
802 pub(crate) lower_read_interrupt: bool,
803 pub(crate) higher_read_interrupt: bool,
804}
805
806pub(crate) struct LoopbackTlsEndpoint {
807 pub(crate) pair: Arc<LoopbackTlsTransportPair>,
808 pub(crate) is_lower_socket: bool,
809 pub(crate) poll_timeout: Duration,
810 pub(crate) registry_key: Option<String>,
817}
818
819#[derive(Debug)]
820pub(crate) struct LoopbackTlsPendingWriteState {
821 pub(crate) buffer: Vec<u8>,
822 pub(crate) warned_near_cap: bool,
823 pub(crate) flushing: bool,
824 pub(crate) defer_shutdown_write: bool,
825 pub(crate) failure_message: Option<String>,
826}
827
828#[derive(Debug, Clone)]
829pub(crate) struct LoopbackTlsPendingWriteHandle {
830 pub(crate) state: Arc<Mutex<LoopbackTlsPendingWriteState>>,
831 pub(crate) tls_handshake_complete: Arc<AtomicBool>,
832 pub(crate) failed: Arc<AtomicBool>,
833 pub(crate) pair: Arc<LoopbackTlsTransportPair>,
834 pub(crate) is_lower_socket: bool,
835 pub(crate) handshake_started_at: Instant,
836}
837
838impl fmt::Debug for LoopbackTlsEndpoint {
839 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
840 f.debug_struct("LoopbackTlsEndpoint")
841 .field("is_lower_socket", &self.is_lower_socket)
842 .finish()
843 }
844}
845
846#[derive(Debug)]
847pub(crate) enum ActiveTlsStream {
848 Client(StreamOwned<ClientConnection, TcpStream>),
849 Server(StreamOwned<ServerConnection, TcpStream>),
850 LoopbackClient(StreamOwned<ClientConnection, LoopbackTlsEndpoint>),
851 LoopbackServer(StreamOwned<ServerConnection, LoopbackTlsEndpoint>),
852}
853
854#[derive(Debug, Clone, Default, Serialize, Deserialize)]
855#[serde(default, rename_all = "camelCase")]
856pub(crate) struct JavascriptTlsClientHello {
857 #[serde(skip_serializing_if = "Option::is_none")]
858 pub(crate) servername: Option<String>,
859 #[serde(
860 rename = "ALPNProtocols",
861 alias = "ALPNProtocols",
862 skip_serializing_if = "Option::is_none"
863 )]
864 pub(crate) alpn_protocols: Option<Vec<String>>,
865}
866
867#[derive(Debug, Clone, Default, Deserialize)]
868#[serde(default, rename_all = "camelCase")]
869pub(crate) struct JavascriptTlsBridgeOptions {
870 pub(crate) is_server: bool,
871 pub(crate) servername: Option<String>,
872 pub(crate) reject_unauthorized: Option<bool>,
873 pub(crate) request_cert: Option<bool>,
874 pub(crate) session: Option<String>,
875 pub(crate) key: Option<JavascriptTlsMaterial>,
876 pub(crate) cert: Option<JavascriptTlsMaterial>,
877 pub(crate) ca: Option<JavascriptTlsMaterial>,
878 pub(crate) passphrase: Option<String>,
879 pub(crate) ciphers: Option<String>,
880 #[serde(alias = "ALPNProtocols")]
881 pub(crate) alpn_protocols: Option<Vec<String>>,
882 pub(crate) min_version: Option<String>,
883 pub(crate) max_version: Option<String>,
884}
885
886#[derive(Debug, Clone, Deserialize)]
887#[serde(untagged)]
888pub(crate) enum JavascriptTlsMaterial {
889 Single(JavascriptTlsDataValue),
890 Many(Vec<JavascriptTlsDataValue>),
891}
892
893#[derive(Debug, Clone, Deserialize)]
894#[serde(tag = "kind", rename_all = "camelCase")]
895pub(crate) enum JavascriptTlsDataValue {
896 Buffer { data: String },
897 String { data: String },
898}
899
900#[derive(Debug, Clone, Default)]
901pub(crate) struct ActiveTlsState {
902 pub(crate) client_hello: Option<JavascriptTlsClientHello>,
903 pub(crate) local_certificates: Vec<Vec<u8>>,
904 pub(crate) session_reused: bool,
905}
906
907#[derive(Debug, Clone, Copy)]
908pub(crate) struct ResolvedTcpConnectAddr {
909 pub(crate) actual_addr: SocketAddr,
910 pub(crate) guest_remote_addr: SocketAddr,
911 pub(crate) use_kernel_loopback: bool,
912}
913
914#[derive(Debug)]
915pub(crate) struct ActiveTcpListener {
916 pub(crate) listener: Option<TcpListener>,
917 pub(crate) kernel_socket_id: Option<SocketId>,
918 pub(crate) local_addr: Option<SocketAddr>,
919 pub(crate) guest_local_addr: SocketAddr,
920 pub(crate) backlog: usize,
921 pub(crate) active_connection_ids: BTreeSet<String>,
922}
923
924#[derive(Debug)]
929pub(crate) enum JavascriptUnixListenerEvent {
930 Connection(PendingUnixSocket),
931 Error {
932 code: Option<String>,
933 message: String,
934 },
935}
936
937#[derive(Debug)]
938pub(crate) struct PendingUnixSocket {
939 pub(crate) stream: UnixStream,
940 pub(crate) local_path: Option<String>,
941 pub(crate) remote_path: Option<String>,
942}
943
944#[derive(Debug)]
945pub(crate) struct ActiveUnixSocket {
946 pub(crate) stream: Arc<Mutex<UnixStream>>,
947 pub(crate) events: Receiver<JavascriptTcpSocketEvent>,
948 pub(crate) event_sender: Sender<JavascriptTcpSocketEvent>,
949 pub(crate) event_pusher: Arc<Mutex<Option<JavascriptSocketEventPusher>>>,
950 pub(crate) listener_id: Option<String>,
951 pub(crate) local_path: Option<String>,
952 pub(crate) remote_path: Option<String>,
953 pub(crate) saw_local_shutdown: Arc<AtomicBool>,
954 pub(crate) saw_remote_end: Arc<AtomicBool>,
955 pub(crate) close_notified: Arc<AtomicBool>,
956}
957
958#[derive(Debug)]
959pub(crate) struct ActiveUnixListener {
960 pub(crate) listener: UnixListener,
961 pub(crate) path: String,
962 pub(crate) backlog: usize,
963 pub(crate) active_connection_ids: BTreeSet<String>,
964}
965
966#[derive(Debug, Clone, Copy, PartialEq, Eq)]
971pub(crate) enum JavascriptUdpFamily {
972 Ipv4,
973 Ipv6,
974}
975
976impl JavascriptUdpFamily {
977 pub(crate) fn from_socket_type(value: &str) -> Result<Self, SidecarError> {
978 match value {
979 "udp4" => Ok(Self::Ipv4),
980 "udp6" => Ok(Self::Ipv6),
981 other => Err(SidecarError::InvalidState(format!(
982 "unsupported dgram socket type {other}"
983 ))),
984 }
985 }
986
987 pub(crate) fn socket_type(self) -> &'static str {
988 match self {
989 Self::Ipv4 => "udp4",
990 Self::Ipv6 => "udp6",
991 }
992 }
993
994 pub(crate) fn matches_addr(self, addr: &SocketAddr) -> bool {
995 matches!(
996 (self, addr),
997 (Self::Ipv4, SocketAddr::V4(_)) | (Self::Ipv6, SocketAddr::V6(_))
998 )
999 }
1000}
1001
1002#[derive(Debug)]
1003pub(crate) enum JavascriptUdpSocketEvent {
1004 Message {
1005 data: Vec<u8>,
1006 remote_addr: SocketAddr,
1007 },
1008 Error {
1009 code: Option<String>,
1010 message: String,
1011 },
1012}
1013
1014#[derive(Debug)]
1018pub(crate) enum PythonHostSocket {
1019 Tcp(TcpStream),
1020 Udp(UdpSocket),
1021}
1022
1023#[derive(Debug)]
1024pub(crate) struct ActiveUdpSocket {
1025 pub(crate) family: JavascriptUdpFamily,
1026 pub(crate) socket: Option<UdpSocket>,
1027 pub(crate) kernel_socket_id: Option<SocketId>,
1028 pub(crate) guest_local_addr: Option<SocketAddr>,
1029 pub(crate) recv_buffer_size: usize,
1030 pub(crate) send_buffer_size: usize,
1031}
1032
1033#[derive(Debug)]
1038pub(crate) enum ActiveExecution {
1039 Javascript(JavascriptExecution),
1040 Python(PythonExecution),
1041 Wasm(Box<WasmExecution>),
1042 Tool(ToolExecution),
1043}
1044
1045#[derive(Debug, Clone)]
1046pub(crate) struct ToolExecution {
1047 pub(crate) cancelled: Arc<AtomicBool>,
1048 pub(crate) pending_events: Arc<Mutex<VecDeque<ActiveExecutionEvent>>>,
1049 pub(crate) events_overflowed: Arc<AtomicBool>,
1050}
1051
1052impl Default for ToolExecution {
1053 fn default() -> Self {
1054 Self {
1055 cancelled: Arc::new(AtomicBool::new(false)),
1056 pending_events: Arc::new(Mutex::new(VecDeque::new())),
1057 events_overflowed: Arc::new(AtomicBool::new(false)),
1058 }
1059 }
1060}
1061
1062#[derive(Debug)]
1063pub(crate) enum ActiveExecutionEvent {
1064 Stdout(Vec<u8>),
1065 Stderr(Vec<u8>),
1066 JavascriptSyncRpcRequest(JavascriptSyncRpcRequest),
1067 PythonVfsRpcRequest(Box<PythonVfsRpcRequest>),
1068 SignalState {
1069 signal: u32,
1070 registration: SignalHandlerRegistration,
1071 },
1072 Exited(i32),
1073}
1074
1075#[derive(Debug)]
1076pub(crate) struct ProcessEventEnvelope {
1077 pub(crate) connection_id: String,
1078 pub(crate) session_id: String,
1079 pub(crate) vm_id: String,
1080 pub(crate) process_id: String,
1081 pub(crate) event: ActiveExecutionEvent,
1082}
1083
1084#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1085pub(crate) enum SocketQueryKind {
1086 TcpListener,
1087 UdpBound,
1088}
1089
1090#[derive(Debug)]
1095pub(crate) struct ResolvedChildProcessExecution {
1096 pub(crate) command: String,
1097 pub(crate) process_args: Vec<String>,
1098 pub(crate) runtime: GuestRuntimeKind,
1099 pub(crate) entrypoint: String,
1100 pub(crate) execution_args: Vec<String>,
1101 pub(crate) env: BTreeMap<String, String>,
1102 pub(crate) guest_cwd: String,
1103 pub(crate) host_cwd: PathBuf,
1104 pub(crate) wasm_permission_tier: Option<WasmPermissionTier>,
1105 pub(crate) tool_command: bool,
1106}
1107
1108#[derive(Debug)]
1113pub(crate) struct ProcNetEntry {
1114 pub(crate) local_host: String,
1115 pub(crate) local_port: u16,
1116 pub(crate) state: String,
1117 pub(crate) inode: u64,
1118}