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:
| Layer | Description |
|---|---|
| App Directory | Always accessible - your app's data folder |
| Agentastic Sandbox | System-wide allow/deny list configured by the user |
| Manifest Permissions | App-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:
- Declare permissions in your
manifest.yaml - 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
| Pattern | Description |
|---|---|
~/Documents | User's Documents folder |
~/Desktop/*.txt | All .txt files on Desktop |
~/Projects/** | Projects folder and all subfolders |
/tmp | System 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
| Error | Description |
|---|---|
PERMISSION_DENIED | Path is outside allowed areas |
FILE_NOT_FOUND | File does not exist |
NOT_A_DIRECTORY | Expected directory, got file |
NO_APP_DIRECTORY | App directory not available |
PATH_ESCAPES_DIRECTORY | Path traversal attempt (e.g., ../) |
ENCODING_ERROR | Failed 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 pathdata(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
- SDK Overview - Introduction to Canvas apps
- Testing & Debugging - Debug your filesystem operations
- API Reference - Complete SDK documentation