gbash

Filesystem

gbash provisions a fresh virtual filesystem for each session. The documented entry points cover the common spectrum from fully isolated in-memory sandboxes to controlled host-backed overlays and custom backends.

Default: In-Memory Filesystem

With no options, gbash.New() creates an isolated in-memory filesystem. Sessions start in /home/agent and all file operations stay in memory.

rt, err := gbash.New()

This is equivalent to:

rt, err := gbash.New(
    gbash.WithFileSystem(gbash.InMemoryFileSystem()),
)

To preload eager or lazy files into that in-memory sandbox, use SeededInMemoryFileSystem:

rt, err := gbash.New(
    gbash.WithFileSystem(gbash.SeededInMemoryFileSystem(gbfs.InitialFiles{
        "/home/agent/config.json": {Content: []byte("{\"mode\":\"dev\"}\n")},
        "/home/agent/large.txt": {
            Lazy: func(ctx context.Context) ([]byte, error) {
                return loadLargeFixture(ctx)
            },
        },
    })),
)

Experimental TrieFS (Read-Mostly)

For static or read-mostly in-memory trees, gbfs also exposes the experimental TrieFS backend. It is opt-in only and is intended for mounted datasets, shared lower layers, and other compositions where reads dominate. It is not the default runtime backend and it is not the recommended path for live host workspace mounts.

The preferred single-root pattern is to seed the trie once and wrap it with Reusable(...) so each session gets a fresh writable overlay above the shared lower layer:

rt, err := gbash.New(
    gbash.WithFileSystem(gbash.CustomFileSystem(
        gbfs.Reusable(gbfs.SeededTrie(gbfs.InitialFiles{
            "/data/catalog/manifest.txt": {Content: []byte("dataset\n")},
            "/data/catalog/index.txt": {
                Lazy: func(ctx context.Context) ([]byte, error) {
                    return loadLargeFixture(ctx)
                },
            },
        })),
        "/data",
    )),
)

MountableFileSystem is the corresponding multi-mount composition point when you want the trie-backed dataset on one mount and mutable storage elsewhere:

rt, err := gbash.New(
    gbash.WithFileSystem(gbash.MountableFileSystem(gbash.MountableFileSystemOptions{
        Base: gbfs.Memory(),
        Mounts: []gbfs.MountConfig{
            {
                MountPoint: "/dataset",
                Factory: gbfs.Reusable(gbfs.SeededTrie(gbfs.InitialFiles{
                    "/docs/guide.txt": {Content: []byte("guide\n")},
                })),
            },
            {MountPoint: "/scratch", Factory: gbfs.Memory()},
        },
        WorkingDir: "/scratch",
    })),
)

Host Directory (Read-Only Overlay)

WithWorkspace mounts a real host directory into the sandbox at /home/agent/project. The host tree is read-only; all writes go to an in-memory overlay so the shell never mutates the host filesystem.

rt, err := gbash.New(
    gbash.WithWorkspace("/path/to/project"),
)

This is the recommended option for embedding gbash against a real codebase. The agent can read project files and create or modify files without affecting the host.

For more control over the mount point or file-read size limits, use HostDirectoryFileSystem directly:

rt, err := gbash.New(
    gbash.WithFileSystem(gbash.HostDirectoryFileSystem("/path/to/project", gbash.HostDirectoryOptions{
        MountPoint:       "/workspace",
        MaxFileReadBytes: 5 << 20, // 5 MB per file
    })),
)

Mountable Namespace

MountableFileSystem composes a base filesystem with one or more sibling mount points inside the sandbox. This is the productized multi-mount path when you want, for example, a workspace, a scratch area, and a fixture tree under one namespace.

rt, err := gbash.New(
    gbash.WithFileSystem(gbash.MountableFileSystem(gbash.MountableFileSystemOptions{
        Mounts: []gbfs.MountConfig{
            {
                MountPoint: "/workspace",
                Factory: gbfs.Overlay(gbfs.Host(gbfs.HostOptions{
                    Root:        "/path/to/project",
                    VirtualRoot: "/",
                })),
            },
            {MountPoint: "/cache", Factory: gbfs.Memory()},
        },
    })),
)

Session.FileSystem() only promises the gbfs.FileSystem interface. Do not rely on concrete backend types or type assertions for mount management.

Custom Filesystem

CustomFileSystem accepts any implementation of fs.Factory and a working directory. This is the low-level escape hatch for custom backends such as database-backed storage.

rt, err := gbash.New(
    gbash.WithFileSystem(gbash.CustomFileSystem(myFactory, "/data")),
)

The fs.Factory interface creates a new fs.FileSystem for each session:

type Factory interface {
    New(ctx context.Context) (FileSystem, error)
}

Overriding the Working Directory

WithWorkingDir changes the initial working directory independently of the filesystem option:

rt, err := gbash.New(
    gbash.WithWorkspace("/path/to/project"),
    gbash.WithWorkingDir("/home/agent/project/src"),
)