microsandbox_protocol/lib.rs
1//! `microsandbox-protocol` defines the shared protocol types used for communication
2//! between the host and the guest agent over CBOR-over-virtio-serial.
3//!
4//! For how the protocol is versioned and evolved while staying backward compatible
5//! across independently-upgraded hosts and live sandboxes, see `VERSIONING.md` in
6//! this crate.
7
8#![warn(missing_docs)]
9
10mod error;
11
12/// Runtime-owned execution jobs on the host-control extension.
13pub mod jobs;
14
15//--------------------------------------------------------------------------------------------------
16// Constants: Host↔Guest Shutdown Timings
17//--------------------------------------------------------------------------------------------------
18
19const HANDOFF_POWEROFF_TIMEOUT_SECS: u64 = 5;
20const SHUTDOWN_FLUSH_MARGIN_SECS: u64 = 3;
21const NORMAL_SHUTDOWN_FLUSH_TIMEOUT_SECS: u64 = 2;
22
23/// Base grace used by explicit host lifetime policies for handoff-init sandboxes.
24///
25/// This is not a graceful Stop deadline. Agentd signals foreign PID 1 once and
26/// leaves its shutdown schedule intact; normal Stop never escalates on a timer.
27pub const HANDOFF_POWEROFF_TIMEOUT: std::time::Duration =
28 std::time::Duration::from_secs(HANDOFF_POWEROFF_TIMEOUT_SECS);
29
30/// Additional host-side margin for an explicitly selected lifetime policy.
31///
32/// Ordinary graceful Stop does not use this fallback margin.
33pub const SHUTDOWN_FLUSH_MARGIN: std::time::Duration =
34 std::time::Duration::from_secs(SHUTDOWN_FLUSH_MARGIN_SECS);
35
36/// Explicit lifetime-policy fallback window when agentd remains PID 1.
37///
38/// agentd can synchronously `sync()`, remount the root read-only, and request
39/// kernel poweroff directly in this mode, so normal development sandboxes
40/// should not pay the longer handoff-init grace.
41pub const NORMAL_SHUTDOWN_FLUSH_TIMEOUT: std::time::Duration =
42 std::time::Duration::from_secs(NORMAL_SHUTDOWN_FLUSH_TIMEOUT_SECS);
43
44/// Explicit lifetime-policy fallback window for a foreign guest PID 1.
45///
46/// Idle, startup-command completion and parent-death policies may bound guest
47/// shutdown before host teardown. Public Stop and its timeout variant do not
48/// install this timer, and agentd no longer sends a fallback SIGTERM.
49///
50/// Equals [`HANDOFF_POWEROFF_TIMEOUT`] plus [`SHUTDOWN_FLUSH_MARGIN`] for the
51/// init's own signal handling — enforced at compile time below.
52pub const HANDOFF_SHUTDOWN_FLUSH_TIMEOUT: std::time::Duration =
53 std::time::Duration::from_secs(HANDOFF_POWEROFF_TIMEOUT_SECS + SHUTDOWN_FLUSH_MARGIN_SECS);
54
55/// Legacy name for the handoff-init shutdown fallback window.
56///
57/// New runtime code should choose between [`NORMAL_SHUTDOWN_FLUSH_TIMEOUT`]
58/// and [`HANDOFF_SHUTDOWN_FLUSH_TIMEOUT`] based on whether a sandbox uses
59/// handoff init.
60pub const SHUTDOWN_FLUSH_TIMEOUT: std::time::Duration = HANDOFF_SHUTDOWN_FLUSH_TIMEOUT;
61
62// Keep a positive host margin in the explicit lifetime-policy window.
63const _: () = assert!(
64 HANDOFF_SHUTDOWN_FLUSH_TIMEOUT.as_secs() > HANDOFF_POWEROFF_TIMEOUT.as_secs(),
65 "HANDOFF_SHUTDOWN_FLUSH_TIMEOUT must exceed HANDOFF_POWEROFF_TIMEOUT",
66);
67
68//--------------------------------------------------------------------------------------------------
69// Constants: Host↔Guest Protocol
70//--------------------------------------------------------------------------------------------------
71
72/// Virtio-console port name for the agent channel.
73pub const AGENT_PORT_NAME: &str = "agent";
74
75/// Virtio-console port name for the optional generation-8 bulk lane.
76#[doc(hidden)]
77pub const AGENT_BULK_PORT_NAME: &str = "agent-bulk";
78
79/// Internal kernel command-line selector for the first dual-port transport profile.
80#[doc(hidden)]
81pub const AGENT_TRANSPORT_DUAL_PORT_CMDLINE: &str = "microsandbox.agent_transport=dual-port-v1";
82
83/// Virtiofs tag for the runtime filesystem (scripts, heartbeat).
84pub const RUNTIME_FS_TAG: &str = "msb_runtime";
85
86/// Guest-write byte budget for the runtime (`/.msb`) virtiofs mount.
87///
88/// `/.msb` is a host↔guest control channel, not bulk storage: the only
89/// guest-written payload is a ~1 KiB heartbeat (host-written scripts and TLS
90/// certs form the mount's baseline and are not charged). This 16 MiB ceiling is
91/// therefore almost entirely abuse headroom — it exists so the channel cannot be
92/// used to fill the host disk. It is intentionally a fixed constant rather than
93/// a user-facing knob.
94pub const RUNTIME_FS_QUOTA_BYTES: u64 = 16 * 1024 * 1024;
95
96/// Default per-device virtio-fs state budget, in MiB.
97pub const FS_STATE_LIMIT_DEFAULT_MIB: u32 = 4;
98
99/// Smallest configurable per-device virtio-fs state budget, in MiB.
100pub const FS_STATE_LIMIT_MIN_MIB: u32 = 1;
101
102/// Largest configurable per-device virtio-fs state budget, in MiB. The state framing is 32-bit.
103pub const FS_STATE_LIMIT_MAX_MIB: u32 = 4095;
104
105/// Guest mount point for the runtime filesystem.
106pub const RUNTIME_MOUNT_POINT: &str = "/.msb";
107
108/// Guest directory for file mount virtiofs shares.
109pub const FILE_MOUNTS_DIR: &str = "/.msb/file-mounts";
110
111/// Guest path for named scripts (added to PATH by agentd).
112pub const SCRIPTS_PATH: &str = "/.msb/scripts";
113
114/// Maximum number of simultaneous SDK clients the host relay admits.
115pub const AGENT_RELAY_MAX_CLIENTS: u32 = 128;
116
117/// Size of the correlation ID range allocated to each relay client.
118pub const AGENT_RELAY_ID_RANGE_STEP: u32 = u32::MAX / AGENT_RELAY_MAX_CLIENTS;
119
120//--------------------------------------------------------------------------------------------------
121// Constants: Guest Init Environment Variables
122//--------------------------------------------------------------------------------------------------
123
124/// Environment variable carrying the sandbox in-guest security profile.
125///
126/// Values:
127/// - `default` — preserve normal guest-root semantics. Exec sessions do not
128/// set `no_new_privs` and keep `CAP_SYS_ADMIN`.
129/// - `restricted` — set `no_new_privs` and drop `CAP_SYS_ADMIN` before user
130/// exec sessions. Agentd also forces `nosuid,nodev` on user mounts.
131///
132/// Example:
133/// - `MSB_SECURITY_PROFILE=restricted`
134pub const ENV_SECURITY_PROFILE: &str = "MSB_SECURITY_PROFILE";
135
136/// Environment variable carrying tmpfs mount specs for guest init.
137///
138/// - `path` — guest mount path (required, always the first element)
139/// - `size=N` — size limit in MiB (optional)
140/// - `noexec` — mount with noexec flag (optional)
141/// - `nosuid` — mount with nosuid flag (optional)
142/// - `nodev` — mount with nodev flag (optional)
143/// - `ro` — mount read-only (optional)
144/// - `rw` — explicit writable default (optional)
145/// - `mode=N` — permission mode as octal integer (optional, e.g. `mode=1777`)
146///
147/// Format: `path[:opts][;path[:opts];...]`.
148///
149/// Entries are separated by `;`. Within an entry, the path comes first,
150/// followed by an optional colon and comma-separated options. Options compose
151/// order-independently (e.g. `:ro,noexec` and `:noexec,ro` are equivalent).
152///
153/// Examples:
154/// - `MSB_TMPFS=/tmp:size=256` — 256 MiB tmpfs at `/tmp`
155/// - `MSB_TMPFS=/tmp:size=256;/var/tmp:size=128` — two tmpfs mounts
156/// - `MSB_TMPFS=/tmp` — tmpfs at `/tmp` with defaults
157/// - `MSB_TMPFS=/tmp:size=256,noexec` — with noexec flag
158/// - `MSB_TMPFS=/seed:size=64,ro` — read-only tmpfs
159pub const ENV_TMPFS: &str = "MSB_TMPFS";
160
161/// Environment variable specifying how agentd assembles the root filesystem.
162///
163/// Format: comma-separated `key=value` pairs, semicolons for multi-value fields.
164///
165/// Variants:
166/// - `kind=disk-image,device=/dev/vda[,fstype=ext4]`
167/// - `kind=oci-layered,lowers=/dev/vdb;/dev/vdc;/dev/vdd,lower_fstype=erofs,upper=/dev/vde,upper_fstype=ext4`
168/// - `kind=oci-flat,lower=/dev/vdb,lower_fstype=erofs,upper=/dev/vdc,upper_fstype=ext4`
169///
170/// Legacy format (`/dev/vda[,fstype=ext4]`) is accepted and treated as `kind=disk-image`.
171pub const ENV_BLOCK_ROOT: &str = "MSB_BLOCK_ROOT";
172
173/// Environment variable carrying the guest network interface configuration.
174///
175/// Format: `key=value,...`
176///
177/// - `iface=NAME` — interface name (required)
178/// - `mac=AA:BB:CC:DD:EE:FF` — MAC address (required)
179/// - `mtu=N` — MTU (optional)
180///
181/// Example:
182/// - `MSB_NET=iface=eth0,mac=02:5a:7b:13:01:02,mtu=1500`
183pub const ENV_NET: &str = "MSB_NET";
184
185/// Environment variable carrying the guest IPv4 network configuration.
186///
187/// Format: `key=value,...`
188///
189/// - `addr=A.B.C.D/N` — address with prefix length (required)
190/// - `gw=A.B.C.D` — default gateway (required)
191/// - `dns=A.B.C.D` — DNS server (optional)
192///
193/// Example:
194/// - `MSB_NET_IPV4=addr=172.16.1.2/30,gw=172.16.1.1,dns=172.16.1.1`
195pub const ENV_NET_IPV4: &str = "MSB_NET_IPV4";
196
197/// Environment variable carrying the guest IPv6 network configuration.
198///
199/// Format: `key=value,...`
200///
201/// - `addr=ADDR/N` — address with prefix length (required)
202/// - `gw=ADDR` — default gateway (required)
203/// - `dns=ADDR` — DNS server (optional)
204///
205/// Example:
206/// - `MSB_NET_IPV6=addr=fd42:6d73:62:2a::2/64,gw=fd42:6d73:62:2a::1,dns=fd42:6d73:62:2a::1`
207pub const ENV_NET_IPV6: &str = "MSB_NET_IPV6";
208
209/// Environment variable carrying virtiofs directory volume mount specs for guest init.
210///
211/// Format: `tag:guest_path[:opts][;tag:guest_path[:opts];...]`
212///
213/// - `tag` — virtiofs tag name (required, matches the tag used in `--mount`)
214/// - `guest_path` — mount point inside the guest (required)
215/// - `ro` / `rw` — access mode option (optional)
216/// - `noexec` — disable direct execution from the mount (optional)
217/// - `nosuid` — mount with nosuid flag (optional)
218/// - `nodev` — mount with nodev flag (optional)
219///
220/// Entries are separated by `;`.
221///
222/// Examples:
223/// - `MSB_DIR_MOUNTS=data:/data` — mount virtiofs tag `data` at `/data`
224/// - `MSB_DIR_MOUNTS=data:/data:ro,noexec` — mount read-only and noexec
225/// - `MSB_DIR_MOUNTS=data:/data;cache:/cache:ro` — two mounts
226pub const ENV_DIR_MOUNTS: &str = "MSB_DIR_MOUNTS";
227
228/// Environment variable carrying virtiofs **file** volume mount specs for guest init.
229///
230/// Used when the host path is a single file rather than a directory. The SDK
231/// asks the runtime to expose the source through a synthetic one-entry
232/// filesystem. Agentd mounts that share at [`FILE_MOUNTS_DIR`]`/<tag>/` and
233/// bind-mounts the file to the guest path.
234///
235/// Format: `tag:filename:guest_path[:opts][;tag:filename:guest_path[:opts];...]`
236///
237/// - `tag` — virtiofs tag name (required, matches the tag used in `--mount`)
238/// - `filename` — name of the file inside the virtiofs share (required)
239/// - `guest_path` — final file path inside the guest (required)
240/// - `ro` / `rw` — access mode option (optional)
241/// - `noexec` — disable direct execution from the mount (optional)
242/// - `nosuid` — mount with nosuid flag (optional)
243/// - `nodev` — mount with nodev flag (optional)
244///
245/// Entries are separated by `;`.
246///
247/// Examples:
248/// - `MSB_FILE_MOUNTS=fm_config:app.conf:/etc/app.conf`
249/// - `MSB_FILE_MOUNTS=fm_config:app.conf:/etc/app.conf:ro,noexec`
250/// - `MSB_FILE_MOUNTS=fm_a:a.sh:/usr/bin/a.sh;fm_b:b.sh:/usr/bin/b.sh`
251pub const ENV_FILE_MOUNTS: &str = "MSB_FILE_MOUNTS";
252
253/// Environment variable carrying disk-image volume mount specs for guest init.
254///
255/// Each spec describes one virtio-blk device attached for the sole purpose
256/// of being mounted at a guest path by agentd (distinct from the rootfs
257/// block device, which is described by [`ENV_BLOCK_ROOT`]).
258///
259/// Format: `id:guest_path[:opts][;id:guest_path[:opts];...]`
260///
261/// - `id` — the `virtio_blk_config.serial` value set by the VMM. Agentd
262/// resolves it to a device node via `/dev/disk/by-id/virtio-<id>`, or
263/// by scanning `/sys/block/*/serial` as a fallback.
264/// - `guest_path` — absolute mount path in the guest (required).
265/// - `fstype=...` — inner filesystem type (optional). When absent,
266/// agentd probes `/proc/filesystems` to find a type that mounts cleanly.
267/// - `ro` / `rw` — access mode option (optional).
268/// - `noexec` — disable direct execution from the mount (optional).
269/// - `nosuid` — mount with nosuid flag (optional).
270/// - `nodev` — mount with nodev flag (optional).
271///
272/// Entries are separated by `;`. Options are comma-separated flags or
273/// key-value pairs in the final option block.
274///
275/// Examples:
276/// - `MSB_DISK_MOUNTS=data_12ab:/data:fstype=ext4` — ext4 disk at `/data`
277/// - `MSB_DISK_MOUNTS=seed_7f:/seed:ro` — autodetect fstype, read-only
278/// - `MSB_DISK_MOUNTS=a_1:/a:fstype=ext4;b_2:/b:ro,noexec` — two disks
279pub const ENV_DISK_MOUNTS: &str = "MSB_DISK_MOUNTS";
280
281/// Environment variable carrying the default guest user for agentd execs.
282///
283/// Format: `USER[:GROUP]` or `UID[:GID]`
284///
285/// - `USER`
286/// - `UID`
287/// - `USER:GROUP`
288/// - `UID:GID`
289///
290/// Example:
291/// - `MSB_USER=alice` — default to user `alice`
292/// - `MSB_USER=1000` — default to UID 1000
293/// - `MSB_USER=alice:developers` — default to user `alice` and group `developers`
294/// - `MSB_USER=1000:100` — default to UID 1000 and GID 100
295pub const ENV_USER: &str = "MSB_USER";
296
297/// Environment variable carrying the guest hostname for agentd.
298///
299/// Format: bare string
300///
301/// Example:
302/// - `MSB_HOSTNAME=worker-01`
303///
304/// agentd calls `sethostname()` and adds the name to `/etc/hosts`.
305/// Defaults to a sandbox-name-derived hostname when not explicitly set.
306pub const ENV_HOSTNAME: &str = "MSB_HOSTNAME";
307
308/// Environment variable carrying the DNS name the guest uses to reach
309/// the sandbox host (Docker's `host.docker.internal` equivalent).
310///
311/// Legacy environment spelling for the host alias now carried in the typed
312/// guest bootstrap. Agentd writes the mapping into `/etc/hosts`. The value the
313/// network stack emits is fixed at `host.microsandbox.internal`.
314pub const ENV_HOST_ALIAS: &str = "MSB_HOST_ALIAS";
315
316/// Environment variable carrying sandbox-wide resource limits.
317///
318/// Format: `resource=limit[:hard][;resource=limit[:hard];...]`
319///
320/// - `resource` — lowercase rlimit name such as `nofile` or `nproc`
321/// - `limit` — soft limit
322/// - `hard` — hard limit (optional; if omitted, uses the soft limit)
323///
324/// Examples:
325/// - `MSB_RLIMITS=nofile=65535`
326/// - `MSB_RLIMITS=nofile=65535:65535;nproc=4096:4096`
327///
328/// agentd applies these during PID 1 startup so every later guest process
329/// inherits the raised baseline instead of having to opt into per-exec rlimits.
330pub const ENV_RLIMITS: &str = "MSB_RLIMITS";
331
332/// Environment variable selecting a guest init binary for PID 1 handoff.
333///
334/// When set, agentd performs initial setup (mounts, runtime dirs), then
335/// forks. The parent execs the binary at this path, becoming the new
336/// PID 1. The child stays alive as a normal grandchild process serving
337/// host requests over virtio-serial.
338///
339/// Format: bare absolute path inside the guest rootfs, or the literal
340/// sentinel [`HANDOFF_INIT_AUTO`] which triggers a candidate probe in
341/// agentd (see [`HANDOFF_INIT_AUTO_CANDIDATES`]).
342///
343/// Examples:
344/// - `MSB_HANDOFF_INIT=/lib/systemd/systemd`
345/// - `MSB_HANDOFF_INIT=auto`
346pub const ENV_HANDOFF_INIT: &str = "MSB_HANDOFF_INIT";
347
348/// Sentinel value for [`ENV_HANDOFF_INIT`] requesting auto-detection.
349///
350/// The host may resolve this sentinel before boot when an OCI image
351/// declares a known init as the first entrypoint token. If the sentinel
352/// reaches the guest unchanged, agentd probes [`HANDOFF_INIT_AUTO_CANDIDATES`]
353/// in order and uses the first path that exists and is executable. If
354/// none match, boot fails with a clear error in `kernel.log` listing the
355/// paths it checked.
356pub const HANDOFF_INIT_AUTO: &str = "auto";
357
358/// Ordered list of image entrypoint paths that `--init auto` may treat
359/// as an explicit handoff init.
360///
361/// This host-side list is intentionally slightly wider than
362/// [`HANDOFF_INIT_AUTO_CANDIDATES`]: `/init` is common in s6-overlay
363/// images but too broad to probe blindly inside every guest rootfs.
364/// Matching it only when the image declares it as ENTRYPOINT keeps the
365/// behavior image-directed.
366pub const HANDOFF_INIT_IMAGE_ENTRYPOINT_CANDIDATES: &[&str] = &[
367 "/init",
368 "/sbin/init",
369 "/lib/systemd/systemd",
370 "/usr/lib/systemd/systemd",
371];
372
373/// Ordered list of init-binary paths agentd probes when
374/// [`ENV_HANDOFF_INIT`] is set to [`HANDOFF_INIT_AUTO`].
375///
376/// Order matters: the first match wins. The list covers the three
377/// well-known locations across major distros:
378/// - `/sbin/init` — BusyBox (Alpine), sysvinit, OpenRC's wrapper.
379/// Usually a symlink to the actual init on systemd distros, so it
380/// resolves naturally on Debian/Ubuntu too.
381/// - `/lib/systemd/systemd` — Debian, Ubuntu, derivatives.
382/// - `/usr/lib/systemd/systemd` — Fedora, RHEL, modern Debian.
383pub const HANDOFF_INIT_AUTO_CANDIDATES: &[&str] = &[
384 "/sbin/init",
385 "/lib/systemd/systemd",
386 "/usr/lib/systemd/systemd",
387];
388
389/// Argv list for the handoff init binary.
390///
391/// Format: base64url-no-padding encoded JSON array of strings.
392/// Empty or unset means the init is exec'd with `argv = [program]`.
393/// This deliberately differs from the delimiter-based `MSB_*` boot env
394/// formats because argv entries are arbitrary strings; wrapping JSON in
395/// base64url preserves spaces, separators, empty strings, and Unicode
396/// without inventing a second escaping language.
397///
398/// Example:
399/// - `MSB_HANDOFF_INIT_ARGS=WyItdW5pdD1tdWx0aS11c2VyLnRhcmdldCJd`
400pub const ENV_HANDOFF_INIT_ARGS: &str = "MSB_HANDOFF_INIT_ARGS";
401
402/// Working directory for the handoff init binary.
403///
404/// Docker applies `WORKDIR` before executing `ENTRYPOINT + CMD`. Init handoff
405/// uses this optional path so image-declared init entrypoints receive the same
406/// process cwd as they would under container startup.
407///
408/// Example:
409/// - `MSB_HANDOFF_INIT_CWD=/opt/app`
410pub const ENV_HANDOFF_INIT_CWD: &str = "MSB_HANDOFF_INIT_CWD";
411
412/// Extra environment variables for the handoff init binary.
413///
414/// Format: base64url-no-padding encoded JSON array of `[key, value]`
415/// pairs. Merged on top of the inherited env.
416/// This uses the same structured payload exception as
417/// [`ENV_HANDOFF_INIT_ARGS`] so env values can contain the delimiter
418/// characters used by older `MSB_*` boot env formats.
419///
420/// Example:
421/// - `MSB_HANDOFF_INIT_ENV=W1siY29udGFpbmVyIiwibWljcm9zYW5kYm94Il1d`
422pub const ENV_HANDOFF_INIT_ENV: &str = "MSB_HANDOFF_INIT_ENV";
423
424/// Guest-side path to the CA certificate for TLS interception.
425///
426/// Placed by the sandbox process via the runtime virtiofs mount.
427/// agentd checks for this file during init and installs it into the guest
428/// trust store.
429pub const GUEST_TLS_CA_PATH: &str = "/.msb/tls/ca.pem";
430
431/// Guest-side path to a PEM bundle of the host's extra trusted CAs.
432///
433/// Placed by the sandbox process via the runtime virtiofs mount when
434/// host-CA trust is enabled (default). agentd checks for this file during
435/// init and appends it to the guest's trust bundle, so outbound TLS works
436/// even behind a corporate MITM proxy whose gateway CA is installed on
437/// the host but unknown to the guest.
438pub const GUEST_TLS_HOST_CAS_PATH: &str = "/.msb/tls/host-cas.pem";
439
440//--------------------------------------------------------------------------------------------------
441// Exports
442//--------------------------------------------------------------------------------------------------
443
444pub mod bootstrap;
445pub mod bulk;
446pub mod codec;
447pub mod control;
448pub mod core;
449pub mod exec;
450pub mod exec_control;
451pub mod fs;
452pub mod heartbeat;
453pub mod message;
454pub mod tcp;
455#[doc(hidden)]
456pub mod transport;
457pub mod wire;
458
459pub use error::*;