# Filesystem

**Added in:** `@mastra/core@1.1.0`

Filesystem providers give agents the ability to read, write, and manage files. When you configure a filesystem on a workspace, agents receive tools for file operations.

A filesystem provider handles all file operations for a workspace:

- **Read** - Read file contents
- **Write** - Create and update files
- **List** - Browse directories with optional glob pattern filtering
- **Delete** - Remove files and directories
- **Stat** - Get file metadata
- **Copy/Move** - Copy or move files between locations
- **Grep** - Search file contents using regex patterns

## Supported providers

Available providers:

- [`LocalFilesystem`](https://mastra.ai/reference/workspace/local-filesystem): Stores files in a directory on disk
- [`S3Filesystem`](https://mastra.ai/reference/workspace/s3-filesystem): Stores files in Amazon S3 or S3-compatible storage (R2, MinIO, Tigris)
- [`GCSFilesystem`](https://mastra.ai/reference/workspace/gcs-filesystem): Stores files in Google Cloud Storage
- [`AzureBlobFilesystem`](https://mastra.ai/reference/workspace/azure-blob-filesystem): Stores files in Azure Blob Storage
- [`AgentFSFilesystem`](https://mastra.ai/reference/workspace/agentfs-filesystem): Stores files in a Turso/SQLite database via AgentFS

> **Tip:** `LocalFilesystem` is the simplest way to get started as it requires no external services. For cloud storage, use `S3Filesystem`, `GCSFilesystem`, or `AzureBlobFilesystem`. For database-backed storage without external services, use `AgentFSFilesystem`.

## Basic usage

Create a workspace with a filesystem and assign it to an agent. The agent can then read, write, and manage files as part of its tasks:

```typescript
import { Agent } from '@mastra/core/agent'
import { Workspace, LocalFilesystem } from '@mastra/core/workspace'

const workspace = new Workspace({
  filesystem: new LocalFilesystem({
    basePath: './workspace',
  }),
})

const agent = new Agent({
  id: 'file-agent',
  model: 'openai/gpt-5.4',
  instructions: 'You are a helpful file management assistant.',
  workspace,
})

// The agent now has filesystem tools available
const response = await agent.generate('List all files in the workspace')
```

## Containment

By default, `LocalFilesystem` runs in **contained mode** — all file operations are restricted to stay within `basePath`. This prevents path traversal attacks and symlink escapes.

In contained mode:

- **Relative paths** (e.g. `src/index.ts`) resolve against `basePath`
- **Absolute paths** (e.g. `/home/user/.config/file.txt`) are treated as real filesystem paths — if they fall outside `basePath` and any `allowedPaths`, a `PermissionError` is thrown
- **Tilde paths** (e.g. `~/Documents`) expand to the home directory and follow the same containment rules

If your agent needs to access specific paths outside `basePath`, use `allowedPaths` to grant access without disabling containment entirely. Relative paths are resolved against `basePath`, and absolute paths are used as-is:

```typescript
const workspace = new Workspace({
  filesystem: new LocalFilesystem({
    basePath: './workspace',
    allowedPaths: ['~/.claude/skills', '../shared-data'],
  }),
})
```

Allowed paths can be updated at runtime using the `setAllowedPaths()` method:

```typescript
// Add a path dynamically
workspace.filesystem.setAllowedPaths(prev => [...prev, '/home/user/documents'])
```

This is the recommended approach for least-privilege access — the agent can only reach the specific directories you allow.

If your agent needs unrestricted access to the entire filesystem, disable containment:

```typescript
const workspace = new Workspace({
  filesystem: new LocalFilesystem({
    basePath: './workspace',
    contained: false,
  }),
})
```

When `contained` is `false`, absolute paths are treated as real filesystem paths with no restriction.

## Dynamic filesystem

The `filesystem` option accepts a resolver function instead of a static instance. The resolver receives `requestContext` and returns a filesystem per request, allowing a single workspace to serve different filesystems based on the caller's identity, role, or tenant.

```typescript
import { Agent } from '@mastra/core/agent'
import { Workspace, LocalFilesystem } from '@mastra/core/workspace'

const workspace = new Workspace({
  filesystem: ({ requestContext }) => {
    const role = requestContext.get('agent-role') || 'guest'
    return new LocalFilesystem({
      basePath: `/workspaces/${role}`,
      readOnly: role !== 'admin',
    })
  },
})

const agent = new Agent({
  id: 'multi-role-agent',
  model: 'openai/gpt-4o',
  workspace,
})
```

Each request resolves its own filesystem at tool execution time:

```typescript
import { RequestContext } from '@mastra/core/request-context'

// Admin request — reads and writes from /workspaces/admin/
const adminCtx = new RequestContext([['agent-role', 'admin']])
await agent.generate('Write report.txt with Q4 results', { requestContext: adminCtx })

// Viewer request — reads from /workspaces/viewer/, writes are blocked
const viewerCtx = new RequestContext([['agent-role', 'viewer']])
await agent.generate('Read info.txt', { requestContext: viewerCtx })
```

The resolver can also be asynchronous, for example to look up configuration from a database:

```typescript
const workspace = new Workspace({
  filesystem: async ({ requestContext }) => {
    const tenantConfig = await db.getTenant(requestContext.get('tenant-id'))
    return new LocalFilesystem({ basePath: tenantConfig.storagePath })
  },
})
```

> **Note:** `filesystem` and `mounts` are mutually exclusive. You cannot use a resolver function together with `mounts` in the same workspace.

## Read-only mode

To prevent agents from modifying files, enable read-only mode:

```typescript
const workspace = new Workspace({
  filesystem: new LocalFilesystem({
    basePath: './workspace',
    readOnly: true,
  }),
})
```

With a static filesystem, write tools (`write_file`, `edit_file`, `delete`, `mkdir`) are excluded from the agent's toolset entirely. The agent can still read and list files.

When using a [dynamic filesystem](#dynamic-filesystem), write tools are always included because `readOnly` isn't known until the resolver runs. Instead, write operations are blocked at runtime — the tool returns an error if the resolved filesystem is read-only.

## Mounts and `CompositeFilesystem`

When you use the `mounts` option on a workspace, Mastra creates a `CompositeFilesystem` that routes file operations to the correct provider based on path prefix.

```typescript
import { Workspace } from '@mastra/core/workspace'
import { S3Filesystem } from '@mastra/s3'
import { GCSFilesystem } from '@mastra/gcs'
import { E2BSandbox } from '@mastra/e2b'

const workspace = new Workspace({
  mounts: {
    '/data': new S3Filesystem({
      bucket: 'my-bucket',
      region: 'us-east-1',
      accessKeyId: process.env.AWS_ACCESS_KEY_ID,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
    }),
    '/skills': new GCSFilesystem({
      bucket: 'agent-skills',
    }),
  },
  sandbox: new E2BSandbox({ id: 'dev-sandbox' }),
})
```

With this configuration:

- `read_file('/data/input.csv')` reads from the S3 bucket
- `write_file('/skills/guide.md', content)` writes to the GCS bucket
- `list_directory('/')` returns virtual entries for `/data` and `/skills`
- Commands in the sandbox can access files at `/data` and `/skills` via FUSE mounts

### Path routing

All file paths must start with a mount prefix. Operations on paths that don't match any mount will fail. Listing the root directory (`/`) returns virtual directory entries for each mount point.

Mount paths can't be nested — for example, you can't mount at both `/data` and `/data/sub`.

### `filesystem` vs `mounts`

`filesystem` and `mounts` are mutually exclusive options on a workspace:

- Use **`filesystem`** when you have a single storage provider and don't need to mount it into a sandbox. The agent gets file tools that operate directly against the provider.
- Use **`mounts`** when you need cloud storage accessible inside a sandbox, or when you want to combine multiple providers. The workspace creates a CompositeFilesystem for file tools and FUSE-mounts the storage into the sandbox.

For local development, you typically don't need `mounts` — a `LocalFilesystem` and `LocalSandbox` pointed at the same directory gives you both file tools and command execution on the same files. See [configuration patterns](https://mastra.ai/docs/workspace/overview) for more detail.

## Agent tools

When you configure a filesystem on a workspace, agents receive tools for reading, writing, listing, and deleting files. See [workspace class reference](https://mastra.ai/reference/workspace/workspace-class) for details.

## Related

- [LocalFilesystem reference](https://mastra.ai/reference/workspace/local-filesystem)
- [S3Filesystem reference](https://mastra.ai/reference/workspace/s3-filesystem)
- [GCSFilesystem reference](https://mastra.ai/reference/workspace/gcs-filesystem)
- [AzureBlobFilesystem reference](https://mastra.ai/reference/workspace/azure-blob-filesystem)
- [AgentFSFilesystem reference](https://mastra.ai/reference/workspace/agentfs-filesystem)
- [Workspace overview](https://mastra.ai/docs/workspace/overview)
- [Sandbox](https://mastra.ai/docs/workspace/sandbox)