greentic_types/
capabilities.rs

1//! Capability and resource declarations shared between manifests and runtimes.
2
3use alloc::{collections::BTreeMap, string::String, vec::Vec};
4
5use crate::{AllowList, NetworkPolicy};
6
7#[cfg(feature = "schemars")]
8use schemars::JsonSchema;
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12/// Declarative capability toggles that packs may request from the runtime.
13#[non_exhaustive]
14#[derive(Clone, Debug, Default, PartialEq, Eq)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16#[cfg_attr(feature = "schemars", derive(JsonSchema))]
17pub struct Capabilities {
18    /// Optional HTTP networking surface (maps to `http.fetch`).
19    #[cfg_attr(
20        feature = "serde",
21        serde(default, skip_serializing_if = "Option::is_none")
22    )]
23    pub http: Option<HttpCaps>,
24    /// Optional secret resolution surface (maps to `secrets.get`).
25    #[cfg_attr(
26        feature = "serde",
27        serde(default, skip_serializing_if = "Option::is_none")
28    )]
29    pub secrets: Option<SecretsCaps>,
30    /// Optional key-value store bindings.
31    #[cfg_attr(
32        feature = "serde",
33        serde(default, skip_serializing_if = "Option::is_none")
34    )]
35    pub kv: Option<KvCaps>,
36    /// Optional filesystem bindings (for embedded assets or scratch space).
37    #[cfg_attr(
38        feature = "serde",
39        serde(default, skip_serializing_if = "Option::is_none")
40    )]
41    pub fs: Option<FsCaps>,
42    /// Optional raw networking permissions.
43    #[cfg_attr(
44        feature = "serde",
45        serde(default, skip_serializing_if = "Option::is_none")
46    )]
47    pub net: Option<NetCaps>,
48    /// Optional tool invocation metadata (for MCP/tool.invoke surfaces).
49    #[cfg_attr(
50        feature = "serde",
51        serde(default, skip_serializing_if = "Option::is_none")
52    )]
53    pub tools: Option<ToolsCaps>,
54}
55
56impl Capabilities {
57    /// Creates an empty capability declaration.
58    pub fn new() -> Self {
59        Self::default()
60    }
61
62    /// Returns `true` when no capabilities are requested.
63    pub fn is_empty(&self) -> bool {
64        self.http.is_none()
65            && self.secrets.is_none()
66            && self.kv.is_none()
67            && self.fs.is_none()
68            && self.net.is_none()
69            && self.tools.is_none()
70    }
71}
72
73/// HTTP capability descriptor controlling outbound fetch settings.
74#[non_exhaustive]
75#[derive(Clone, Debug, Default, PartialEq, Eq)]
76#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
77#[cfg_attr(feature = "schemars", derive(JsonSchema))]
78pub struct HttpCaps {
79    /// Optional allow list applied before requests are dispatched.
80    #[cfg_attr(
81        feature = "serde",
82        serde(default, skip_serializing_if = "Option::is_none")
83    )]
84    pub allow_list: Option<AllowList>,
85    /// Maximum request/response body size in bytes (when enforced).
86    #[cfg_attr(
87        feature = "serde",
88        serde(default, skip_serializing_if = "Option::is_none")
89    )]
90    pub max_body_bytes: Option<u64>,
91}
92
93impl HttpCaps {
94    /// Creates an empty descriptor.
95    pub fn new() -> Self {
96        Self::default()
97    }
98}
99
100/// Secret capability descriptor enumerating runtime-provided handles.
101#[non_exhaustive]
102#[derive(Clone, Debug, Default, PartialEq, Eq)]
103#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
104#[cfg_attr(feature = "schemars", derive(JsonSchema))]
105pub struct SecretsCaps {
106    /// Secret identifiers that must be bound before execution.
107    #[cfg_attr(
108        feature = "serde",
109        serde(default, skip_serializing_if = "Vec::is_empty")
110    )]
111    pub required: Vec<String>,
112}
113
114impl SecretsCaps {
115    /// Creates an empty descriptor.
116    pub fn new() -> Self {
117        Self::default()
118    }
119}
120
121/// Key-value capability descriptor for packs that need durable storage.
122#[non_exhaustive]
123#[derive(Clone, Debug, Default, PartialEq, Eq)]
124#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
125#[cfg_attr(feature = "schemars", derive(JsonSchema))]
126pub struct KvCaps {
127    /// Allowed logical namespaces.
128    #[cfg_attr(
129        feature = "serde",
130        serde(default, skip_serializing_if = "Vec::is_empty")
131    )]
132    pub namespaces: Vec<String>,
133}
134
135impl KvCaps {
136    /// Creates an empty descriptor.
137    pub fn new() -> Self {
138        Self::default()
139    }
140}
141
142/// Filesystem bindings exposed to packs.
143#[non_exhaustive]
144#[derive(Clone, Debug, PartialEq, Eq)]
145#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
146#[cfg_attr(feature = "schemars", derive(JsonSchema))]
147pub struct FsCaps {
148    /// List of host paths mapped into the pack sandbox.
149    #[cfg_attr(
150        feature = "serde",
151        serde(default, skip_serializing_if = "Vec::is_empty")
152    )]
153    pub paths: Vec<String>,
154    /// Whether the paths should be mounted read-only.
155    #[cfg_attr(feature = "serde", serde(default = "FsCaps::default_read_only"))]
156    pub read_only: bool,
157}
158
159impl Default for FsCaps {
160    fn default() -> Self {
161        Self {
162            paths: Vec::new(),
163            read_only: true,
164        }
165    }
166}
167
168impl FsCaps {
169    const fn default_read_only() -> bool {
170        true
171    }
172
173    /// Creates an empty descriptor.
174    pub fn new() -> Self {
175        Self::default()
176    }
177}
178
179/// Low-level networking descriptor (raw sockets, tunnels, etc.).
180#[non_exhaustive]
181#[derive(Clone, Debug, Default, PartialEq, Eq)]
182#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
183#[cfg_attr(feature = "schemars", derive(JsonSchema))]
184pub struct NetCaps {
185    /// Network policy enforced before the runtime opens connections.
186    #[cfg_attr(
187        feature = "serde",
188        serde(default, skip_serializing_if = "Option::is_none")
189    )]
190    pub policy: Option<NetworkPolicy>,
191}
192
193impl NetCaps {
194    /// Creates an empty descriptor.
195    pub fn new() -> Self {
196        Self::default()
197    }
198}
199
200/// Tool invocation descriptor for packs relying on host tools.
201#[non_exhaustive]
202#[derive(Clone, Debug, Default, PartialEq, Eq)]
203#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
204#[cfg_attr(feature = "schemars", derive(JsonSchema))]
205pub struct ToolsCaps {
206    /// Tool identifiers the pack expects the host to resolve.
207    #[cfg_attr(
208        feature = "serde",
209        serde(default, skip_serializing_if = "Vec::is_empty")
210    )]
211    pub allowed: Vec<String>,
212}
213
214impl ToolsCaps {
215    /// Creates an empty descriptor.
216    pub fn new() -> Self {
217        Self::default()
218    }
219}
220
221/// Resource limit declarations respected by runtimes.
222#[non_exhaustive]
223#[derive(Clone, Debug, PartialEq, Eq)]
224#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
225#[cfg_attr(feature = "schemars", derive(JsonSchema))]
226pub struct Limits {
227    /// Memory ceiling per flow instance (in megabytes).
228    pub memory_mb: u32,
229    /// Wall-clock budget per invocation (milliseconds).
230    pub wall_time_ms: u64,
231    /// Optional fuel/step counter for deterministic engines.
232    #[cfg_attr(
233        feature = "serde",
234        serde(default, skip_serializing_if = "Option::is_none")
235    )]
236    pub fuel: Option<u64>,
237    /// Optional file descriptor ceiling.
238    #[cfg_attr(
239        feature = "serde",
240        serde(default, skip_serializing_if = "Option::is_none")
241    )]
242    pub files: Option<u32>,
243}
244
245impl Limits {
246    /// Creates a new limit declaration.
247    pub fn new(memory_mb: u32, wall_time_ms: u64) -> Self {
248        Self {
249            memory_mb,
250            wall_time_ms,
251            fuel: None,
252            files: None,
253        }
254    }
255}
256
257impl Default for Limits {
258    fn default() -> Self {
259        Self::new(0, 0)
260    }
261}
262
263/// Telemetry publishing configuration shared by hosts and packs.
264#[non_exhaustive]
265#[derive(Clone, Debug, PartialEq, Eq)]
266#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
267#[cfg_attr(feature = "schemars", derive(JsonSchema))]
268pub struct TelemetrySpec {
269    /// Prefix applied to spans emitted by the pack.
270    pub span_prefix: String,
271    /// Static key/value attributes added to every span/log record.
272    #[cfg_attr(feature = "serde", serde(default))]
273    pub attributes: BTreeMap<String, String>,
274    /// Whether the runtime should emit per-node spans automatically.
275    pub emit_node_spans: bool,
276}
277
278impl TelemetrySpec {
279    /// Creates a telemetry specification with the provided prefix.
280    pub fn new(span_prefix: impl Into<String>) -> Self {
281        Self {
282            span_prefix: span_prefix.into(),
283            attributes: BTreeMap::new(),
284            emit_node_spans: false,
285        }
286    }
287}
288
289impl Default for TelemetrySpec {
290    fn default() -> Self {
291        Self::new("greentic")
292    }
293}