Back to Microsandbox

Filesystem

docs/sdk/go/filesystem.mdx

0.5.1026.0 KB
Original Source

Read and write files inside a running sandbox over the same host-guest channel as command execution: no SSH, no network. See Filesystem for usage examples. For bulk file transfer, prefer a volume.

<div className="msb-glance"> <p className="msb-gl"><span className="msb-dot instance"></span>Methods · SandboxFSOps<span className="msb-ct">16</span></p> <a className="msb-row" href="#fs-read"><span className="msb-rn">fs.Read()</span><span className="msb-rg">read a file as bytes</span></a> <a className="msb-row" href="#fs-readstring"><span className="msb-rn">fs.ReadString()</span><span className="msb-rg">read a file as a string</span></a> <a className="msb-row" href="#fs-write"><span className="msb-rn">fs.Write()</span><span className="msb-rg">write bytes</span></a> <a className="msb-row" href="#fs-writestring"><span className="msb-rn">fs.WriteString()</span><span className="msb-rg">write a string</span></a> <a className="msb-row" href="#fs-list"><span className="msb-rn">fs.List()</span><span className="msb-rg">list a directory</span></a> <a className="msb-row" href="#fs-stat"><span className="msb-rn">fs.Stat()</span><span className="msb-rg">file metadata</span></a> <a className="msb-row" href="#fs-mkdir"><span className="msb-rn">fs.Mkdir()</span><span className="msb-rg">create a directory</span></a> <a className="msb-row" href="#fs-remove"><span className="msb-rn">fs.Remove()</span><span className="msb-rg">delete a file</span></a> <a className="msb-row" href="#fs-removedir"><span className="msb-rn">fs.RemoveDir()</span><span className="msb-rg">delete a directory</span></a> <a className="msb-row" href="#fs-copy"><span className="msb-rn">fs.Copy()</span><span className="msb-rg">copy within the guest</span></a> <a className="msb-row" href="#fs-rename"><span className="msb-rn">fs.Rename()</span><span className="msb-rg">rename / move</span></a> <a className="msb-row" href="#fs-exists"><span className="msb-rn">fs.Exists()</span><span className="msb-rg">test a path</span></a> <a className="msb-row" href="#fs-copyfromhost"><span className="msb-rn">fs.CopyFromHost()</span><span className="msb-rg">host file into guest</span></a> <a className="msb-row" href="#fs-copytohost"><span className="msb-rn">fs.CopyToHost()</span><span className="msb-rg">guest file to host</span></a> <a className="msb-row" href="#fs-readstream"><span className="msb-rn">fs.ReadStream()</span><span className="msb-rg">stream a large read</span></a> <a className="msb-row" href="#fs-writestream"><span className="msb-rn">fs.WriteStream()</span><span className="msb-rg">stream a large write</span></a> <p className="msb-gl"><span className="msb-dot type"></span>Types</p> <div className="msb-chiprow"> <a className="msb-typepill" href="#fsentry">FsEntry</a> <a className="msb-typepill" href="#fsentrykind">FsEntryKind</a> <a className="msb-typepill" href="#fsstat">FsStat</a> <a className="msb-typepill" href="#fsreadstream">FsReadStream</a> <a className="msb-typepill" href="#fswritestream">FsWriteStream</a> </div> </div> <p className="msb-label" id="typical-flow">Typical flow</p>
go
import m "github.com/superradcompany/microsandbox/sdk/go"

fs := sb.FS()                                                     // 1. get the accessor

err := fs.WriteString(ctx, "/tmp/config.json", `{"debug":true}`)  // 2. write
if err != nil {
    return err
}

content, err := fs.ReadString(ctx, "/tmp/config.json")            // 3. read back
if err != nil {
    return err
}
fmt.Println(content)

Methods

The accessor is obtained from a running sandbox via sb.FS(). Every method takes a context.Context first and returns an error (wrapped, inspectable with m.IsKind).


<span className="msb-recv">fs.</span><span className="msb-hn">Read()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) Read(ctx context.Context, path string) ([]byte, error)

Read the entire contents of a file as raw bytes. The default FFI buffer is 1 MiB; for files larger than ~750 KiB (after base64 inflation) the runtime returns BufferTooSmall on the single-shot path, and this method transparently falls back to ReadStream so callers get a uniform bytes-returning interface up to runtime memory limits.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the read.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">string</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">[]byte</span></div> <div className="msb-param-desc">File contents.</div> </div> </div> <Accordion title="Example">
go
data, err := fs.Read(ctx, "/etc/hostname")
if err != nil {
    return err
}
fmt.Printf("%d bytes\n", len(data))
</Accordion>

<span className="msb-recv">fs.</span><span className="msb-hn">ReadString()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) ReadString(ctx context.Context, path string) (string, error)

Read a file and return its contents as a string. The bytes are reinterpreted as UTF-8 without validation.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the read.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">string</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.</div> </div> </div> <Accordion title="Example">
go
content, err := fs.ReadString(ctx, "/tmp/config.json")
</Accordion>

<span className="msb-recv">fs.</span><span className="msb-hn">Write()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) Write(ctx context.Context, path string, data []byte) error

Write bytes to a file, creating it if it does not exist and truncating it if it does.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the write.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">string</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">[]byte</span></div> <div className="msb-param-desc">Bytes to write.</div> </div> </div> <Accordion title="Example">
go
err := fs.Write(ctx, "/tmp/data.bin", []byte{0x00, 0x01, 0x02})
</Accordion>

<span className="msb-recv">fs.</span><span className="msb-hn">WriteString()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) WriteString(ctx context.Context, path, content string) error

Write a UTF-8 string to a file. Convenience wrapper over Write.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the write.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Absolute path inside the guest.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>content</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Text to write.</div> </div> </div> <Accordion title="Example">
go
err := fs.WriteString(ctx, "/tmp/hello.txt", "hi")
</Accordion>

<span className="msb-recv">fs.</span><span className="msb-hn">List()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) List(ctx context.Context, path string) ([]FsEntry, error)

List the entries in a directory inside the sandbox.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the listing.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">string</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">[]FsEntry</a></div> <div className="msb-param-desc">Directory entries.</div> </div> </div> <Accordion title="Example">
go
entries, err := fs.List(ctx, "/etc")
if err != nil {
    return err
}
for _, e := range entries {
    fmt.Printf("%s (%s, %d bytes)\n", e.Path, e.Kind, e.Size)
}
</Accordion>

<span className="msb-recv">fs.</span><span className="msb-hn">Stat()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) Stat(ctx context.Context, path string) (*FsStat, error)

Get detailed metadata for a file or directory.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the stat.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">string</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="#fsstat">*FsStat</a></div> <div className="msb-param-desc">File metadata.</div> </div> </div> <Accordion title="Example">
go
st, err := fs.Stat(ctx, "/etc/hosts")
if err != nil {
    return err
}
fmt.Printf("%d bytes, dir=%v\n", st.Size, st.IsDir)
</Accordion>

<span className="msb-recv">fs.</span><span className="msb-hn">Mkdir()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) Mkdir(ctx context.Context, path string) error

Create a directory and any missing parents inside the sandbox.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the operation.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Absolute directory path inside the guest.</div> </div> </div> <Accordion title="Example">
go
err := fs.Mkdir(ctx, "/app/cache/sessions")
</Accordion>

<span className="msb-recv">fs.</span><span className="msb-hn">Remove()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) Remove(ctx context.Context, path string) error

Delete a single file. Use RemoveDir for directories.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the operation.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Absolute file path inside the guest.</div> </div> </div> <Accordion title="Example">
go
err := fs.Remove(ctx, "/tmp/scratch.txt")
</Accordion>

<span className="msb-recv">fs.</span><span className="msb-hn">RemoveDir()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) RemoveDir(ctx context.Context, path string) error

Remove a directory recursively.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the operation.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Absolute directory path inside the guest.</div> </div> </div> <Accordion title="Example">
go
err := fs.RemoveDir(ctx, "/app/cache")
</Accordion>

<span className="msb-recv">fs.</span><span className="msb-hn">Copy()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) Copy(ctx context.Context, src, dst string) error

Copy a file within the sandbox.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the operation.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>src</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Source path inside the guest.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>dst</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Destination path inside the guest.</div> </div> </div> <Accordion title="Example">
go
err := fs.Copy(ctx, "/etc/hosts", "/tmp/hosts.bak")
</Accordion>

<span className="msb-recv">fs.</span><span className="msb-hn">Rename()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) Rename(ctx context.Context, src, dst string) error

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>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the operation.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>src</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Current path inside the guest.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>dst</code><span className="msb-type">string</span></div> <div className="msb-param-desc">New path inside the guest.</div> </div> </div> <Accordion title="Example">
go
err := fs.Rename(ctx, "/tmp/old.log", "/tmp/archive/old.log")
</Accordion>

<span className="msb-recv">fs.</span><span className="msb-hn">Exists()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) Exists(ctx context.Context, path string) (bool, error)

Report whether a file or directory exists at the given path.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the check.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">string</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">bool</span></div> <div className="msb-param-desc"><code>true</code> if the path exists.</div> </div> </div> <Accordion title="Example">
go
ok, err := fs.Exists(ctx, "/app/.initialized")
if err != nil {
    return err
}
if !ok {
    // first boot
}
</Accordion>

<span className="msb-recv">fs.</span><span className="msb-hn">CopyFromHost()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) CopyFromHost(ctx context.Context, hostPath, guestPath string) error

Copy a file from the host machine into the sandbox. 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>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the copy.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>hostPath</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Source path on the host.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>guestPath</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Destination path inside the guest.</div> </div> </div> <Accordion title="Example">
go
err := fs.CopyFromHost(ctx, "./seed.db", "/app/data/seed.db")
</Accordion>

<span className="msb-recv">fs.</span><span className="msb-hn">CopyToHost()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) CopyToHost(ctx context.Context, guestPath, hostPath string) error

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>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels the copy.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>guestPath</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Source path inside the guest.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>hostPath</code><span className="msb-type">string</span></div> <div className="msb-param-desc">Destination path on the host.</div> </div> </div> <Accordion title="Example">
go
err := fs.CopyToHost(ctx, "/app/output/report.csv", "./report.csv")
</Accordion>

<span className="msb-recv">fs.</span><span className="msb-hn">ReadStream()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) ReadStream(ctx context.Context, path string) (*FsReadStream, error)

Open a streaming reader for a file. Use this for files too large to fit in memory; data is delivered in chunks. The caller must Close the returned *FsReadStream. Read falls back to this automatically when a file exceeds the single-shot buffer.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels opening the stream.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">string</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">Open read stream; close it when done.</div> </div> </div> <Accordion title="Example">
go
rs, err := fs.ReadStream(ctx, "/var/log/syslog")
if err != nil {
    return err
}
defer rs.Close()

for {
    chunk, err := rs.Recv(ctx)
    if err != nil {
        return err
    }
    if chunk == nil {
        break // EOF
    }
    os.Stdout.Write(chunk)
}

// Or drain it as an io.WriterTo.
_, err = rs.WriteTo(os.Stdout)
</Accordion>

<span className="msb-recv">fs.</span><span className="msb-hn">WriteStream()</span>

<div className="msb-tags"><span className="msb-tag is-instance">method</span></div>
go
func (fs *SandboxFSOps) WriteStream(ctx context.Context, path string) (*FsWriteStream, error)

Open a streaming writer for a file. Use this for files too large to fit in memory. The caller must call Close(ctx) on the returned *FsWriteStream to finalise the write.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>ctx</code><span className="msb-type">context.Context</span></div> <div className="msb-param-desc">Cancels opening the stream.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">string</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="#fswritestream">*FsWriteStream</a></div> <div className="msb-param-desc">Open write stream; <code>Close(ctx)</code> it to finalise.</div> </div> </div> <Accordion title="Example">
go
ws, err := fs.WriteStream(ctx, "/tmp/big.bin")
if err != nil {
    return err
}

if _, err := ws.Write(largeChunk); err != nil {
    return err
}
if err := ws.Close(ctx); err != nil {
    return err
}
</Accordion>

Types

FsEntry

<div className="msb-tags"><span className="msb-tag is-type">struct</span></div> <p className="msb-backref">Returned by <a href="#fs-list">List()</a></p>

A single directory listing entry.

FieldTypeDescription
PathstringFile path
KindFsEntryKindEntry type
Sizeint64File size in bytes
Modeuint32Unix permission bits

FsEntryKind

<div className="msb-tags"><span className="msb-tag is-type">type</span></div> <p className="msb-backref">Used by <a href="#fsentry">FsEntry.Kind</a></p>

Classifies a directory listing entry. Defined as type FsEntryKind string.

ConstantValueDescription
FsEntryKindFile"file"Regular file
FsEntryKindDirectory"directory"Directory
FsEntryKindSymlink"symlink"Symbolic link
FsEntryKindOther"other"Other entry type

FsStat

<div className="msb-tags"><span className="msb-tag is-type">struct</span></div> <p className="msb-backref">Returned by <a href="#fs-stat">Stat()</a></p>

Detailed file metadata.

FieldTypeDescription
PathstringFile path
Sizeint64File size in bytes
Modeuint32Unix permission bits
ModTimetime.TimeLast modified timestamp; zero value if the guest did not report one
IsDirboolWhether the path is a directory

FsReadStream

<div className="msb-tags"><span className="msb-tag is-type">struct</span></div> <p className="msb-backref">Returned by <a href="#fs-readstream">ReadStream()</a></p>

An open streaming read from a guest file. Must be closed with Close when done.

MethodReturnsDescription
Recv(ctx context.Context)([]byte, error)Receive the next chunk; returns (nil, nil) at EOF
WriteTo(w io.Writer)(int64, error)Drain the stream into w using context.Background(); implements io.WriterTo
CopyTo(ctx context.Context, w io.Writer)(int64, error)Drain into w honouring ctx for per-chunk cancellation
Close()errorRelease the read stream handle

On a write error or partial write, WriteTo and CopyTo return the partial byte count and leave the stream open so the caller can recover; close it explicitly in either case.

FsWriteStream

<div className="msb-tags"><span className="msb-tag is-type">struct</span></div> <p className="msb-backref">Returned by <a href="#fs-writestream">WriteStream()</a></p>

An open streaming write to a guest file. Must be closed with Close(ctx) to finalise the write.

MethodReturnsDescription
Write(p []byte)(int, error)Send a chunk; implements io.Writer, uses context.Background() internally
WriteCtx(ctx context.Context, data []byte)errorSend a chunk with explicit context control
Close(ctx context.Context)errorSend the EOF marker and wait for the guest to confirm; must be called to complete the write