microsandbox_types/exec.rs
1//! Sandbox-owned execution admission and I/O policy.
2
3use serde::{Deserialize, Serialize};
4
5//--------------------------------------------------------------------------------------------------
6// Constants
7//--------------------------------------------------------------------------------------------------
8
9/// Memory-accounting charge in addition to each output payload.
10pub const EXEC_OUTPUT_EVENT_OVERHEAD: u32 = 256;
11
12//--------------------------------------------------------------------------------------------------
13// Types
14//--------------------------------------------------------------------------------------------------
15
16/// Effective policy shared by every execution in one sandbox.
17///
18/// The values are fixed defaults chosen by the runtime; a resumed memory snapshot keeps its
19/// captured policy. Connected clients discover the effective policy in Ready; execution
20/// requests cannot change it.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
22#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
23#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
24#[serde(default)]
25pub struct ExecLimits {
26 /// Maximum admitted executions, including exited streams with unread output.
27 pub max_executions: u32,
28 /// Maximum stdin bytes not yet delivered into the command's pipe.
29 pub input_limit: u32,
30 /// Maximum unconsumed stdout/stderr allowance, including per-message overhead.
31 pub output_limit: u32,
32 /// Maximum payload of one stdin message.
33 pub stdin_chunk_size: u32,
34 /// Maximum payload of one stdout or stderr message.
35 pub output_chunk_size: u32,
36}
37
38//--------------------------------------------------------------------------------------------------
39// Methods
40//--------------------------------------------------------------------------------------------------
41
42impl ExecLimits {
43 /// Reject policies whose chunks cannot make progress within their allowances.
44 pub fn validate(&self) -> Result<(), &'static str> {
45 // The protocol frame ceiling is 4 MiB; leave space for its envelope.
46 const MAX_CHUNK_SIZE: u32 = 4 * 1024 * 1024 - 1024;
47 if self.stdin_chunk_size > MAX_CHUNK_SIZE || self.output_chunk_size > MAX_CHUNK_SIZE {
48 return Err("execution chunk sizes must fit within the protocol frame ceiling");
49 }
50 if self.max_executions == 0 {
51 return Err("exec.max_executions must be positive");
52 }
53 if self.stdin_chunk_size == 0 || self.stdin_chunk_size > self.input_limit {
54 return Err("exec.stdin_chunk_size must be positive and fit within exec.input_limit");
55 }
56 let output_charge = self
57 .output_chunk_size
58 .checked_add(EXEC_OUTPUT_EVENT_OVERHEAD);
59 if self.output_chunk_size == 0 || output_charge.is_none_or(|n| n > self.output_limit) {
60 return Err(
61 "exec.output_chunk_size plus message overhead must fit within exec.output_limit",
62 );
63 }
64 Ok(())
65 }
66}
67
68//--------------------------------------------------------------------------------------------------
69// Trait Implementations
70//--------------------------------------------------------------------------------------------------
71
72impl Default for ExecLimits {
73 fn default() -> Self {
74 Self {
75 max_executions: 128,
76 input_limit: 64 * 1024,
77 output_limit: 1024 * 1024,
78 stdin_chunk_size: 16 * 1024,
79 output_chunk_size: 16 * 1024,
80 }
81 }
82}