adk_sandbox/backend.rs
1//! The [`SandboxBackend`] trait and [`BackendCapabilities`] descriptor.
2
3use async_trait::async_trait;
4
5use crate::error::SandboxError;
6use crate::types::{ExecRequest, ExecResult, Language};
7
8/// Async trait for isolated code execution backends.
9///
10/// Implementations provide a single `execute()` method that runs code in
11/// isolation and returns the result. The trait is intentionally minimal —
12/// no lifecycle methods (start/stop/restart). `ProcessBackend` is stateless
13/// and `WasmBackend` creates a fresh instance per call.
14///
15/// # Example
16///
17/// ```rust,ignore
18/// use adk_sandbox::{SandboxBackend, ExecRequest, ExecResult, SandboxError};
19///
20/// struct MyBackend;
21///
22/// #[async_trait::async_trait]
23/// impl SandboxBackend for MyBackend {
24/// fn name(&self) -> &str { "my-backend" }
25/// fn capabilities(&self) -> BackendCapabilities { /* ... */ }
26/// async fn execute(&self, request: ExecRequest) -> Result<ExecResult, SandboxError> {
27/// // Execute code in isolation
28/// todo!()
29/// }
30/// }
31/// ```
32#[async_trait]
33pub trait SandboxBackend: Send + Sync {
34 /// Returns the backend name (e.g., `"process"`, `"wasm"`).
35 fn name(&self) -> &str;
36
37 /// Returns the capabilities and enforced limits of this backend.
38 fn capabilities(&self) -> BackendCapabilities;
39
40 /// Executes code in isolation according to the request parameters.
41 ///
42 /// # Errors
43 ///
44 /// Returns [`SandboxError`] if execution cannot complete (timeout,
45 /// memory exceeded, invalid request, etc.). A non-zero exit code
46 /// is **not** an error — it is returned in [`ExecResult::exit_code`].
47 async fn execute(&self, request: ExecRequest) -> Result<ExecResult, SandboxError>;
48}
49
50/// Describes what a backend supports and enforces.
51///
52/// Callers can inspect capabilities to choose the right backend or to
53/// understand what isolation guarantees are provided.
54///
55/// # Example
56///
57/// ```rust
58/// use adk_sandbox::{BackendCapabilities, EnforcedLimits, Language};
59///
60/// let caps = BackendCapabilities {
61/// supported_languages: vec![Language::Python, Language::JavaScript],
62/// isolation_class: "process".to_string(),
63/// enforced_limits: EnforcedLimits {
64/// timeout: true,
65/// memory: false,
66/// network_isolation: false,
67/// filesystem_write_isolation: false,
68/// filesystem_read_isolation: false,
69/// environment_isolation: true,
70/// },
71/// };
72/// assert!(caps.enforced_limits.timeout);
73/// ```
74#[derive(Debug, Clone)]
75pub struct BackendCapabilities {
76 /// Languages this backend can execute.
77 pub supported_languages: Vec<Language>,
78 /// Isolation class identifier (e.g., `"process"`, `"wasm"`, `"container"`).
79 pub isolation_class: String,
80 /// Which resource limits the backend actually enforces.
81 pub enforced_limits: EnforcedLimits,
82}
83
84/// Describes which resource limits a backend enforces.
85///
86/// Backends are honest about what they enforce. For example,
87/// `ProcessBackend` enforces timeout and environment isolation
88/// but not memory or network isolation.
89#[derive(Debug, Clone)]
90pub struct EnforcedLimits {
91 /// Whether the backend enforces execution timeout.
92 pub timeout: bool,
93 /// Whether the backend enforces memory limits.
94 pub memory: bool,
95 /// Whether the backend isolates network access.
96 pub network_isolation: bool,
97 /// Whether the backend prevents writes outside the policy's allowed paths.
98 pub filesystem_write_isolation: bool,
99 /// Whether the backend prevents *reads* outside the policy's allowed paths.
100 ///
101 /// Separate from write isolation because they are not equivalent and the platforms
102 /// differ. The macOS Seatbelt profile denies writes, network, and fork but leaves
103 /// reads open, so code can read host files outside the allowed paths even though it
104 /// cannot modify them. Reporting one `filesystem_isolation` flag hid that.
105 pub filesystem_read_isolation: bool,
106 /// Whether the backend isolates environment variables.
107 pub environment_isolation: bool,
108}