Skip to main content

Composable virtual filesystem sandbox with a UNIX-like command surface for agents

Project description

sandfs

sandfs is an experimental Python package that implements an entirely virtual filesystem (VFS) that can be embedded inside AI agent tooling. Each sandbox keeps its own private directory tree, supports dynamic nodes that are hydrated on demand, and exposes a small UNIX-like command surface (cd, ls, cat, grep, rg, etc.) so agents can reuse familiar workflows.

Status: design prototype. APIs are subject to change.

Why?

Agent builders frequently need a scratch filesystem for planning, iterating on generated code, and testing hypotheses. Shipping a simple exec tool that proxies every bash command to the host disk is unsafe. sandfs keeps those operations inside a controlled, in-memory sandbox while still feeling like a mini Linux environment.

Features

  • In-memory directories and files that never touch the host disk unless exported.
  • Dynamic nodes backed by callables (e.g., query a DB, fetch from an API, or generate text on the fly).
  • Pure-Python shell/executor that understands a handful of GNU-style commands and can be extended with your own.
  • Optional Python execution helper that evaluates snippets against the sandbox state only.
  • Bridge to real GNU utilities via host -p <path> <command> which materializes the subtree and runs the host binary against it.
  • Node policies (read-only, append-only, visibility labels) plus shell views so different agents see only the nodes they are allowed to.
  • Write hooks and optimistic versions so hosts can flush files to external stores with conflict detection.
  • Storage adapters and snapshots so you can mount external state and roll forward/back within a turn.
  • Serialization helpers to snapshot or hydrate sandboxes (planned).

Installation

pip install git+https://github.com/lydakis/sandfs.git
# Once a PyPI release is available: pip install sandfs

Quickstart

from sandfs import VirtualFileSystem, SandboxShell

vfs = VirtualFileSystem()
vfs.write_file("/notes/todo.txt", "- build VFS\n- add rg support\n")

shell = SandboxShell(vfs)
print(shell.exec("ls /notes").stdout)
print(shell.exec("rg 'VFS' /notes").stdout)

Agent shell usage

from sandfs import ProvidedNode, SandboxShell, VirtualFileSystem

vfs = VirtualFileSystem()
vfs.write_file("/workspace/app.py", "print('hello')\n")

def logs_provider(ctx):
    return {"latest.log": ProvidedNode.file(content="generated on demand")}

vfs.mount_directory("/workspace/logs", logs_provider)

shell = SandboxShell(vfs)
shell.exec("cd /workspace")
print(shell.exec("ls").stdout)
print(shell.exec("tree").stdout)

Running host GNU tools

SandboxShell exposes host to materialize a subtree onto the host disk and invoke any installed command:

host -p /workspace grep -n TODO app.py

The example above exports /workspace into a temporary directory, runs the system grep inside it, then discards the files.

Tip: unknown commands automatically fall back to the host runner, so echo, python3, or bash -lc '…' work even if they are not native sandfs commands.

Policies & views

vfs.set_policy(
    "/blue/identity/persona.md",
    NodePolicy(writable=False, classification="private", principals={"alice", "bob"}),
)
shell = SandboxShell(vfs, view=VisibilityView(classifications={"public"}, principals={"alice"}))

Agent shell mode

shell = SandboxShell(
    vfs,
    allowed_commands={"ls", "cat", "rg"},
    max_output_bytes=8_192,
)

Commands outside allowed_commands now return an error immediately, and outputs larger than max_output_bytes are rejected so partner-facing agents cannot dump oversized payloads.

Storage adapters

from sandfs import MemoryStorageAdapter, VirtualFileSystem

adapter = MemoryStorageAdapter(initial={"a.txt": "seed"})
vfs = VirtualFileSystem()
vfs.mount_storage("/data", adapter)

print(vfs.read_file("/data/a.txt"))
vfs.write_file("/data/a.txt", "update")
vfs.sync_storage("/data")  # refresh from adapter if it changed externally

mount_storage keeps the virtual tree synchronized with the adapter, while sync_storage refreshes the VFS from the latest adapter contents.

Snapshots

snap = vfs.snapshot()
# run risky operations ...
vfs.restore(snap)

Snapshots capture the entire tree (including storage-backed nodes) so you can checkpoint before an agent action and roll back on failure.

Repository layout

.
├── docs/               # design/vision notes
├── sandfs/             # library sources
└── tests/              # runtime smoke tests

Local development

python -m venv .venv
source .venv/bin/activate
uv pip install -e .[dev]
uv run pytest

Persistence hooks

Register a write hook to flush files into your own store and use optimistic versions to avoid clobbering concurrent updates:

from sandfs import VirtualFileSystem
from sandfs.hooks import WriteEvent

vfs = VirtualFileSystem()

def flush(event: WriteEvent) -> None:
    save_to_db(event.path, event.content, event.version)

vfs.register_write_hook("/blue/work", flush)
vfs.write_file("/blue/work/note.md", "draft")
vfs.write_file("/blue/work/note.md", "final", expected_version=1)

Integration hooks

from sandfs.integrations import InboxRecorder

recorder = InboxRecorder()
recorder.attach(vfs, "/blue/inbox")

PathEvent (used under the hood) carries the path, event type (create, update, delete), and latest content so you can fan writes into staging queues or audit logs.

License

MIT

Further reading

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sandfs-0.2.0.tar.gz (20.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sandfs-0.2.0-py3-none-any.whl (19.8 kB view details)

Uploaded Python 3

File details

Details for the file sandfs-0.2.0.tar.gz.

File metadata

  • Download URL: sandfs-0.2.0.tar.gz
  • Upload date:
  • Size: 20.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for sandfs-0.2.0.tar.gz
Algorithm Hash digest
SHA256 910b595fb1a1582b8f73237ee8a5f6168fcfe869e34b7c480ce56e66c0faf3f5
MD5 c91d424be3a6b98ae379e19e11f1d6b6
BLAKE2b-256 dc2a2da1599409fbf596c46c0c09ee87e54b8c3e9e80f485537f0e707886a004

See more details on using hashes here.

Provenance

The following attestation bundles were made for sandfs-0.2.0.tar.gz:

Publisher: release.yml on lydakis/sandfs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sandfs-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: sandfs-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 19.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for sandfs-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4cd1e270f550e25cccae7fa6cead87b142b4f33ee186ba148f5255d009b3a337
MD5 a7d918d19f8addbfc0b301b9cd26d7a9
BLAKE2b-256 8ca37329b4c023982119f5ab3465a6ca27cf3d709e1830e95862d7f2403cb16c

See more details on using hashes here.

Provenance

The following attestation bundles were made for sandfs-0.2.0-py3-none-any.whl:

Publisher: release.yml on lydakis/sandfs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page