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