Docs

Filesystem

The Canvas App SDK provides sandboxed filesystem access through the Agentastic.fs namespace. Apps can read, write, and manage files within their own directory, with optional access to external paths through explicit permissions.

Security Model

Canvas apps operate within a layered security model:

LayerDescription
App DirectoryAlways accessible - your app's data folder
Agentastic SandboxSystem-wide allow/deny list configured by the user
Manifest PermissionsApp-declared paths for external access

App Directory

Every Canvas app has its own directory where it can freely read and write files:

~/Library/Application Support/agentastic/apps/<app-slug>/

For example, an app installed under the folder name notes would have:

~/Library/Application Support/agentastic/apps/notes/
├── manifest.yaml
├── index.html
├── data/
│   └── notes.json
└── cache/
    └── temp.txt

No permissions required for accessing files within this directory.

External Path Access

To access files outside your app directory, you must:

  1. Declare permissions in your manifest.yaml
  2. Pass the Agentastic sandbox - paths must be in the user's allow list

Basic Usage

Reading Files

// Read a file from your app directory (always allowed)
const data = await Agentastic.fs.read('data/notes.json');
console.log(data); // File contents as string

// Read a binary file
const image = await Agentastic.fs.read('assets/logo.png');
if (image.type === 'binary') {
  const base64 = image.data; // Base64-encoded content
}

Writing Files

// Write text to a file
await Agentastic.fs.write('data/notes.json', JSON.stringify(notes));

// Write binary data
await Agentastic.fs.write('cache/image.png', {
  type: 'binary',
  data: base64EncodedString
});

Checking File Existence

const exists = await Agentastic.fs.exists('data/config.json');
if (!exists) {
  await Agentastic.fs.write('data/config.json', '{}');
}

Listing Directory Contents

const files = await Agentastic.fs.list('data');
console.log(files); // ['notes.json', 'settings.json']

Manifest Permissions

To access paths outside your app directory, declare them in manifest.yaml:

id: my-notes-app
name: My Notes
version: 1.0.0
entrypoint: index.html

permissions:
  filesystem:
    read:
      - ~/Documents/Notes
      - ~/Desktop/*.txt
    write:
      - ~/Documents/Notes

Permission Patterns

PatternDescription
~/DocumentsUser's Documents folder
~/Desktop/*.txtAll .txt files on Desktop
~/Projects/**Projects folder and all subfolders
/tmpSystem temp directory

Glob Patterns

  • * - Matches any characters except /
  • ** - Matches any characters including / (recursive)
permissions:
  filesystem:
    read:
      - ~/Documents/**/*.md    # All markdown files in Documents
      - ~/Pictures/*.{png,jpg} # Images on Pictures root
    write:
      - ~/Documents/MyApp      # Specific folder

Access Examples

Given an app at ~/Library/Application Support/agentastic/apps/dev.example.notes/:

// ALLOWED - within app directory
await Agentastic.fs.read('config.json');           // Relative path
await Agentastic.fs.read('./data/notes.json');     // Explicit relative
await Agentastic.fs.write('cache/temp.txt', data); // Write to subdirectory

// ALLOWED - absolute path to app directory
const appPath = '~/Library/Application Support/agentastic/apps/dev.example.notes/data.json';
await Agentastic.fs.read(appPath);

// BLOCKED - outside app directory without permissions
await Agentastic.fs.read('/etc/hosts');           // System file
await Agentastic.fs.read('~/Documents/secret.txt'); // User files

// ALLOWED - with proper manifest permissions
// (requires permissions.filesystem.read: ['~/Documents'])
await Agentastic.fs.read('~/Documents/notes.txt');

Error Handling

The filesystem API throws errors for permission violations and file issues:

try {
  const content = await Agentastic.fs.read('~/Documents/file.txt');
} catch (error) {
  switch (error.code) {
    case 'PERMISSION_DENIED':
      console.error('No permission to access this path');
      break;
    case 'FILE_NOT_FOUND':
      console.error('File does not exist');
      break;
    case 'NOT_A_DIRECTORY':
      console.error('Expected a directory');
      break;
    default:
      console.error('Filesystem error:', error.message);
  }
}

Error Types

ErrorDescription
PERMISSION_DENIEDPath is outside allowed areas
FILE_NOT_FOUNDFile does not exist
NOT_A_DIRECTORYExpected directory, got file
NO_APP_DIRECTORYApp directory not available
PATH_ESCAPES_DIRECTORYPath traversal attempt (e.g., ../)
ENCODING_ERRORFailed to encode/decode data

System Sandbox

Canvas apps inherit the Agentastic file tool's security sandbox. Even with manifest permissions, access to these paths is always blocked:

  • /System - macOS system files
  • /private - Private system data
  • /usr/bin, /usr/sbin - System binaries
  • /bin, /sbin - Core binaries
  • /Library/Security - Security frameworks
  • /Library/Keychains - Keychain data
  • /.Trash - Trash folder

Users can configure additional restrictions in the Agentastic settings.

Best Practices

1. Use Relative Paths

Prefer relative paths for app data - they're more portable:

// Good - relative to app directory
await Agentastic.fs.read('data/settings.json');

// Avoid - hardcoded absolute paths
await Agentastic.fs.read('/Users/john/Library/.../settings.json');

2. Handle Errors Gracefully

Always wrap filesystem operations in try-catch:

async function loadSettings() {
  try {
    const data = await Agentastic.fs.read('settings.json');
    return JSON.parse(data);
  } catch (error) {
    if (error.code === 'FILE_NOT_FOUND') {
      return {}; // Return defaults
    }
    throw error; // Re-throw unexpected errors
  }
}

3. Request Minimal Permissions

Only request access to paths you actually need:

# Good - specific paths
permissions:
  filesystem:
    read:
      - ~/Documents/MyApp

# Avoid - overly broad access
permissions:
  filesystem:
    read:
      - ~/Documents
      - ~/Desktop
      - ~/Downloads

4. Create Subdirectories for Organization

// Organize your app's data
await Agentastic.fs.write('data/notes/2024-01-15.json', noteData);
await Agentastic.fs.write('cache/images/thumb-123.png', imageData);
await Agentastic.fs.write('config/preferences.json', prefs);

API Reference

Agentastic.fs.read(path)

Reads a file's contents.

Parameters:

  • path (string) - File path (relative or absolute)

Returns:

  • string - Text file contents, or
  • { type: 'binary', data: string } - Base64-encoded binary data

Agentastic.fs.write(path, data)

Writes data to a file. Creates parent directories if needed.

Parameters:

  • path (string) - File path
  • data (string | object) - Content to write

Agentastic.fs.exists(path)

Checks if a file or directory exists.

Parameters:

  • path (string) - Path to check

Returns:

  • boolean - True if path exists

Agentastic.fs.list(path)

Lists directory contents.

Parameters:

  • path (string) - Directory path

Returns:

  • string[] - Array of file/folder names

Next Steps