docs/sdk/rust/filesystem.mdx
Read, write, list, and manipulate files inside a running sandbox. Obtained via sb.fs(). Every op dispatches through the same host-guest channel as command execution, so there is no SSH and no network involved. For bulk file movement, prefer a volume that gives the guest direct filesystem access. See Filesystem for conceptual usage.
use microsandbox::Sandbox;
let sb = Sandbox::builder("api").image("python").create().await?;
let fs = sb.fs();
fs.write("/tmp/config.json", r#"{"debug": true}"#).await?;
let text = fs.read_to_string("/tmp/config.json").await?;
println!("{text}");
for entry in fs.list("/tmp").await? {
println!("{} ({} bytes)", entry.path, entry.size);
}
sb.fs() is synchronous and just borrows the sandbox's backend and name; the work happens on the awaited methods below, every one of which is async.
async fn read(&self, path: &str) -> MicrosandboxResult<Bytes>
Read an entire file from the guest filesystem into memory as raw bytes.
<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">&str</span></div> <div className="msb-param-desc">Absolute path inside the guest, e.g. <code>"/app/config.json"</code>.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">Bytes</span></div> <div className="msb-param-desc">File contents as raw bytes.</div> </div> </div> <Accordion title="Example">let bytes = sb.fs().read("/app/logo.png").await?;
println!("{} bytes", bytes.len());
async fn read_to_string(&self, path: &str) -> MicrosandboxResult<String>
Read an entire file and decode it as UTF-8. Errors if the contents are not valid UTF-8.
<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">&str</span></div> <div className="msb-param-desc">Absolute path inside the guest.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">String</span></div> <div className="msb-param-desc">File contents as a UTF-8 string.</div> </div> </div> <Accordion title="Example">let text = sb.fs().read_to_string("/etc/hostname").await?;
println!("{}", text.trim());
async fn read_stream(&self, path: &str) -> MicrosandboxResult<FsReadStream>
Open a streaming reader that yields chunks of file data as they arrive. Use this for files too large to hold in memory, or to process data incrementally.
<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">&str</span></div> <div className="msb-param-desc">Absolute path inside the guest.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#fsreadstream">FsReadStream</a></div> <div className="msb-param-desc">Reader that yields chunks until the file is exhausted.</div> </div> </div> <Accordion title="Example">let mut stream = sb.fs().read_stream("/var/log/big.log").await?;
let mut total = 0;
while let Some(chunk) = stream.recv().await? {
total += chunk.len();
}
println!("read {total} bytes");
async fn write(&self, path: &str, data: impl AsRef<[u8]>) -> MicrosandboxResult<()>
Write data to a file in the guest, creating it if it doesn't exist and truncating it if it does. Parent directories must already exist.
<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">&str</span></div> <div className="msb-param-desc">Absolute path inside the guest.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>data</code><span className="msb-type">impl AsRef<[u8]></span></div> <div className="msb-param-desc">File content (bytes or a string).</div> </div> </div> <Accordion title="Example">sb.fs().write("/tmp/hello.txt", "hi there").await?;
async fn write_stream(&self, path: &str) -> MicrosandboxResult<FsWriteSink>
Open a streaming writer for large files. Write chunks incrementally, then call FsWriteSink::close() to flush and finalize. The file is created if missing and truncated if it exists.
let sink = sb.fs().write_stream("/tmp/out.bin").await?;
sink.write(&[0u8; 4096]).await?;
sink.write(&[1u8; 4096]).await?;
sink.close().await?;
async fn list(&self, path: &str) -> MicrosandboxResult<Vec<FsEntry>>
List the immediate children of a directory (non-recursive). Each entry carries its path, kind, size, mode, and modification time.
<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">&str</span></div> <div className="msb-param-desc">Absolute directory path inside the guest.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#fsentry">Vec<FsEntry></a></div> <div className="msb-param-desc">Directory entries.</div> </div> </div> <Accordion title="Example">for entry in sb.fs().list("/app").await? {
println!("{:?} {}", entry.kind, entry.path);
}
async fn mkdir(&self, path: &str) -> MicrosandboxResult<()>
Create a directory, including any missing parent directories.
<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">&str</span></div> <div className="msb-param-desc">Absolute directory path inside the guest.</div> </div> </div> <Accordion title="Example">sb.fs().mkdir("/app/data/cache").await?;
async fn remove_dir(&self, path: &str) -> MicrosandboxResult<()>
Remove a directory and everything under it, recursively. For a single file use remove().
sb.fs().remove_dir("/app/data/cache").await?;
async fn remove(&self, path: &str) -> MicrosandboxResult<()>
Delete a single file. Use remove_dir() for directories.
sb.fs().remove("/tmp/hello.txt").await?;
async fn copy(&self, from: &str, to: &str) -> MicrosandboxResult<()>
Copy a file from one path to another within the sandbox.
<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>from</code><span className="msb-type">&str</span></div> <div className="msb-param-desc">Source path inside the guest.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>to</code><span className="msb-type">&str</span></div> <div className="msb-param-desc">Destination path inside the guest.</div> </div> </div> <Accordion title="Example">sb.fs().copy("/app/config.json", "/app/config.bak.json").await?;
async fn rename(&self, from: &str, to: &str) -> MicrosandboxResult<()>
Rename or move a file or directory within the sandbox.
<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>from</code><span className="msb-type">&str</span></div> <div className="msb-param-desc">Current path inside the guest.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>to</code><span className="msb-type">&str</span></div> <div className="msb-param-desc">New path inside the guest.</div> </div> </div> <Accordion title="Example">sb.fs().rename("/tmp/draft.txt", "/tmp/final.txt").await?;
async fn stat(&self, path: &str) -> MicrosandboxResult<FsMetadata>
Get metadata for a file or directory: kind, size, mode, read-only flag, and timestamps. A final symlink is followed (equivalent to stat_with_follow(path, true)); use stat_with_follow() to control that.
let meta = sb.fs().stat("/app/config.json").await?;
println!("{} bytes, mode {:o}", meta.size, meta.mode);
async fn stat_with_follow(&self, path: &str, follow_symlink: bool) -> MicrosandboxResult<FsMetadata>
Get metadata, choosing whether to follow a final symlink. With follow_symlink = false you stat the link itself rather than its target. Local backend only; cloud returns Unsupported.
let link_meta = sb.fs().stat_with_follow("/app/current", false).await?;
println!("{:?}", link_meta.kind);
async fn set_stat(&self, path: &str, follow_symlink: bool, attrs: FsSetAttrs) -> MicrosandboxResult<()>
Update metadata on a file or directory: mode, owner uid/gid, size, and access/modification times. Only the fields you set on FsSetAttrs are applied. Local backend only; cloud returns Unsupported.
use microsandbox::sandbox::FsSetAttrs;
sb.fs().set_stat("/app/run.sh", true, FsSetAttrs {
mode: Some(0o755),
..Default::default()
}).await?;
async fn read_link(&self, path: &str) -> MicrosandboxResult<String>
Read the target of a symbolic link, returning the literal target text. Local backend only; cloud returns Unsupported.
let target = sb.fs().read_link("/app/current").await?;
println!("-> {target}");
async fn symlink(&self, target: &str, link_path: &str) -> MicrosandboxResult<()>
Create a symbolic link at link_path that points to target. Local backend only; cloud returns Unsupported.
sb.fs().symlink("/app/releases/v2", "/app/current").await?;
async fn exists(&self, path: &str) -> MicrosandboxResult<bool>
Check whether a file or directory exists at the given path in the guest. Implemented as a stat() probe: a successful stat yields true, a filesystem-op error yields false, and transport errors still propagate.
if sb.fs().exists("/app/config.json").await? {
println!("config present");
}
async fn copy_from_host(&self, host_path: impl AsRef<Path>, guest_path: &str) -> MicrosandboxResult<()>
Copy a file from the host machine into the sandbox, streaming it in chunks. For transferring many files, consider a bind-mounted volume instead.
<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>host_path</code><span className="msb-type">impl AsRef<Path></span></div> <div className="msb-param-desc">Path on the host filesystem.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>guest_path</code><span className="msb-type">&str</span></div> <div className="msb-param-desc">Destination path inside the sandbox.</div> </div> </div> <Accordion title="Example">sb.fs().copy_from_host("./local/model.bin", "/app/model.bin").await?;
async fn copy_to_host(&self, guest_path: &str, host_path: impl AsRef<Path>) -> MicrosandboxResult<()>
Copy a file from the sandbox to the host machine.
<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>guest_path</code><span className="msb-type">&str</span></div> <div className="msb-param-desc">Path inside the sandbox.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>host_path</code><span className="msb-type">impl AsRef<Path></span></div> <div className="msb-param-desc">Destination path on the host.</div> </div> </div> <Accordion title="Example">sb.fs().copy_to_host("/app/out/report.pdf", "./report.pdf").await?;
Filesystem operations handle for a running sandbox, borrowing the parent sandbox's backend and name (it is generic over a lifetime, SandboxFs<'a>). Every method dispatches through the same host-guest channel as command execution. Local routes to core.fs.* agent messages; cloud returns Unsupported per method until cloud guest-fs lands. All operations are listed in the sections above.
with_backend(backend, name) is a public constructor for FFI shims that re-assemble a SandboxFs per call; most callers obtain one via sb.fs().
Metadata for a single entry returned from a directory listing.
| Field | Type | Description |
|---|---|---|
| path | String | Path of the entry |
| kind | FsEntryKind | Kind of entry |
| size | u64 | Size in bytes |
| mode | u32 | Unix permission bits (e.g. 0o644) |
| modified | Option<DateTime<Utc>> | Last modification time |
The kind of a filesystem entry. Derives Copy, PartialEq, and Eq.
| Variant | Description |
|---|---|
File | Regular file |
Directory | Directory |
Symlink | Symbolic link |
Other | Other entry type (device, socket, etc.) |
Detailed metadata for a file or directory.
| Field | Type | Description |
|---|---|---|
| kind | FsEntryKind | Kind of entry |
| size | u64 | Size in bytes |
| mode | u32 | Unix permission bits |
| readonly | bool | Whether the entry is read-only (no owner write bit) |
| modified | Option<DateTime<Utc>> | Last modification time |
| created | Option<DateTime<Utc>> | Creation time (not populated by guest stat, currently always None) |
Attributes accepted by set_stat(). Re-exported from microsandbox_protocol::fs. Derives Default, so set only the fields you want to change and spread the rest with ..Default::default(). Each field is Option: None leaves that attribute unchanged.
| Field | Type | Description |
|---|---|---|
| mode | Option<u32> | Unix permission bits |
| uid | Option<u32> | Owner user ID |
| gid | Option<u32> | Owner group ID |
| size | Option<u64> | File size (truncate or extend) |
| atime | Option<i64> | Access time as Unix timestamp seconds |
| mtime | Option<i64> | Modification time as Unix timestamp seconds |
Streaming reader for file data from the sandbox. Obtained via read_stream().
| Method | Signature | Description |
|---|---|---|
| recv() | recv(&mut self) -> MicrosandboxResult<Option<Bytes>> | Receive the next chunk; None once the file is fully read. Errors if the guest reports a failure. |
| collect() | collect(self) -> MicrosandboxResult<Bytes> | Consume the stream and collect all remaining chunks into a single buffer. |
Streaming writer for file data to the sandbox. Obtained via write_stream().
| Method | Signature | Description |
|---|---|---|
| write() | write(&self, data: impl AsRef<[u8]>) -> MicrosandboxResult<()> | Write a chunk of data. |
| close() | close(self) -> MicrosandboxResult<()> | Send EOF and wait for confirmation. Must be called to finalize the write; errors if the guest reports a write failure. |
Type alias for an agentd-side filesystem handle. Internal plumbing: no public method takes or returns it, and the streaming types hold one only behind their own state.
pub type FsHandle = u64;