Skip to main content

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}