Files and rootfs
File transfer
client.Files (IFileClient) copies files and directories between the local machine and a sandbox. The files travel as a tar stream over exec, so the sandbox image needs tar and sh.
// Single files: the remote path names the file, and its parent directory is created.
await client.Files.UploadAsync("agent-1", "settings.json", "/workspace/config/settings.json");
await client.Files.DownloadAsync("agent-1", "/workspace/out/report.csv", "report.csv");
// Streams, with the Unix mode of the uploaded file.
using (var script = new MemoryStream(Encoding.UTF8.GetBytes("#!/bin/sh\necho ready\n")))
{
await client.Files.UploadAsync(
"agent-1",
script,
"/workspace/bin/start.sh",
UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
}
await using (var destination = File.Create("app.log"))
{
await client.Files.DownloadAsync("agent-1", "/var/log/app.log", destination);
}
// Whole directories.
await client.Files.UploadDirectoryAsync("agent-1", "./src", "/workspace/src");
await client.Files.DownloadDirectoryAsync("agent-1", "/workspace/out", "./out");
- Uploads create the remote parent directories. A remote file path must name a file, not end with
/. - A non-seekable upload stream is buffered in memory first, because the tar header needs the size up front. Uploaded stream files default to mode
0644unless you pass one. DownloadAsyncto a path writes a temporary file next to the target and replaces the target only on success.DownloadDirectoryAsyncrefuses archive entries that would land outside the target directory, and links.- A failing remote
tarduring an upload throws OpenShellException with its exit code and error output; a download whose archive is truncated, empty, or unexpected throwsInvalidDataException.
Rootfs staging
Compute drivers that boot from a root filesystem archive (for example the VM driver) read it from a gateway staging slot. client.Rootfs (IRootfsClient) stages a local tar archive and returns a token; RootfsDriverConfig.WithStagingToken puts the token into the template's driver configuration.
// Copies the archive into a gateway staging slot (the client must share the gateway's filesystem)
// and returns the token that names it.
var token = await client.Rootfs.StageAsync("/srv/images/agent-rootfs.tar");
// Boot a sandbox from it: the token goes into the driver config of the template.
var sandbox = await client.Sandboxes.CreateAsync("vm-agent", new SandboxSpec
{
Template = new SandboxTemplateSpec
{
DriverConfigJson = RootfsDriverConfig.WithStagingToken(token),
},
});
Only a client that shares the gateway's filesystem can complete the copy, so this is a local-gateway workflow. The gateway enforces a size limit and reclaims unused slots (RootfsStagingSlot.ExpiresAt). BeginStagingAsync allocates a slot without copying, when you want to write the archive yourself. WithStagingToken keeps any settings already in the driver configuration JSON you pass, and throws when it already contains a token.