Skip to main content
The S3 VFS mounts an Amazon S3 bucket (or any S3-compatible service) at some prefix such as /s3/. All operations involve network I/O to the remote object store. Uses aioboto3 for async S3 access. Compatible with: AWS S3, MinIO, Cloudflare R2, Supabase Storage, DigitalOcean Spaces, and any S3-compatible service. Install the extra:

Config

S3VFS(config) takes an S3Config object with the bucket name and optional credentials, endpoint, and connection settings. Both READ and WRITE modes are supported. Fields marked secret are redacted in snapshots.

VFS.md

The VFS.md an agent reads with S3 mounted at /<path>.

Virtual Filesystem

Supported Unix Commands for VFS

Unix commands work on each path as on a local filesystem, as far as the path supports them:

Limitations of Unix Commands for VFS

Where a path differs from a local filesystem:Run man <cmd> or <cmd> --help for one command’s usage.
The S3 VFS maps S3 object keys to virtual paths under the mount prefix. S3 “directories” are prefix-based - there are no real directory objects. For example, if bucket my-bucket contains:
Then mounting at /s3/ exposes:
Path mapping: virtual /s3/data/file.txt maps to S3 key data/file.txt.

Cache

The S3 VFS uses IndexCacheStore with index_ttl = 600 (10 minutes). Directory listings are cached for up to 600 seconds before being refreshed from S3. This reduces API calls for repeated directory traversals. A configured index: replaces this lifetime with its own ttl, and the mount’s ttl: caps it.

Conditional writes

Under write: conditional a write carries the version of the file mirage read, and S3 refuses it when the file has changed since. The endpoint decides which writes can be conditioned: AWS’s conditions every write, copy and delete; any other endpoint_url (or AWS_ENDPOINT_URL_S3/AWS_ENDPOINT_URL) only a write, so mirage refuses what would copy or delete there. An endpoint set only in an AWS profile is not read: give a non-AWS server its endpoint_url. If the session last saw no file at the path, the write sends If-None-Match: * and fails if someone created the file in the meantime. A conditional single-file move attempts rollback when source deletion is refused. The move still fails and warns if the destination may remain. Cleanup uses the copy’s S3 version or, for a new destination, its ETag. Unversioned overwrites, same-ETag replacements, retried versioned copies, and uncertain delete outcomes limit recovery; inspect both paths after a failure. An absent destination must remain absent until copying. This guard may add one metadata request; cleanup adds one delete and may require s3:DeleteObjectVersion. Directory and cross-mount moves do not use rollback. A directory mv, rm -r or rmdir looks at the directory again when it is done: a file written into it that the command never listed stays, and the command fails with Directory not empty. Under write: conditional, a directory mv that finds a file changed before it could copy it removes nothing from the source; the copies it made stay at the destination. An append or another rewrite that observes a missing file uses create-only commit under either write policy. S3 checks If-None-Match: * at PUT or final multipart completion, so a delayed creation cannot replace a competing writer’s bytes. A lost condition returns an error without automatic retry. Existing-file appends under unconditional retain last-write-wins behavior.

Refused keys

When credentials cannot read an S3 object (S3 returns AccessDenied, or a bodiless HTTP 403 response on a HEAD request), Mirage reports EACCES (Permission denied) for that file operand. Shell commands follow standard POSIX conventions:
  • cat a b c prints the contents of a and c, writes a permission error for b to stderr, and exits with a non-zero status.
  • grep -r logs an error on inaccessible files and exits with status 2.
  • cp b x || fallback triggers the fallback branch.
  • test -e b evaluates to false.

IAM permissions and existence checks

  • Scoped grants: When access is granted below a specific prefix (for example, s3:GetObject on bucket/dir/* alongside s3:ListBucket on the bucket), Mirage treats dir as a directory during stat.
  • Missing ListBucket: Without s3:ListBucket permission, S3 returns HTTP 403 even for non-existent keys to avoid leaking existence. As a result, non-existent files report Permission denied (EACCES) instead of No such file or directory (ENOENT).
  • Listed but unreadable keys: If ListObjectsV2 includes an object that your credentials cannot read, stat and test -e report that the file exists, while read attempts (cat) fail with Permission denied.

Integration

What S3 answers through its own API, so fewer files are read. Integration explains each command.

Example

Shell Commands

The S3 VFS supports the full set of shell commands since it operates on real file content (text, binary, JSON, CSV, etc.). Large files benefit from range reads to avoid downloading entire objects.

Customize a builtin command

The generic command definitions are available before a workspace is created. Copy the definition with the behavior you want to change, then register that copy on one mount:
The builtin definition and other mounts stay unchanged. Registration is a host application action; an agent can discover and run the resulting cat command, but cannot alter the command table.

Read Commands

Text Processing

File Operations

Path Utilities

Compression

Encoding

Use Cases

  • AI agents accessing cloud data: Mount S3 buckets for agents to read and process remote datasets
  • Data pipelines: Read and write S3 objects with shell-like commands
  • Sandboxed cloud storage access: Restrict agent operations to a specific bucket and prefix
  • FUSE mounting: Expose S3 buckets through a virtual FUSE mount for external tools

Scoping a VFS to a key prefix

Pass key_prefix: str | None = None to S3Config to transparently scope every operation to a subpath of the bucket:
When set, every read/write/list/stat/copy/rename/delete operation is transparently scoped to that bucket subpath. Agents see clean paths like /data/notes.md; the underlying bucket key is users/{user_id}/data/notes.md. Useful for multi-tenant systems. Pairs naturally with STS AssumeRole session policies for AWS-side enforcement. Unset behavior is unchanged. Normalization: leading slashes are stripped and a trailing slash is added automatically. Both None and an empty string are treated as “no prefix.”