Gives an agent a folder to read and write: /workspace. It also writes to the shared /context folder, where other apps can find files by what they're for, and announces every new file on the context bus.
cd apps/filesystem
berth dev # boots it in a sandbox, reloads on save
berth test # builds the production image, checks the exports, runs the app's own testsberth is the CLI: npm install -g @berthos/cli, or node ../../packages/cli/bin/berth.js from a clone. Call an export with berth rpc filesystem --export list_files, or connect an MCP client with berth mcp --app filesystem (add --only=read_file,write_file,list_files to bridge just those exports). See the MCP quickstart.
capabilities:
- filesystem:read:/workspace
- filesystem:write:/workspace
- filesystem:read:/context
- filesystem:write:/contextNo network:* capability is declared, so the app can't open any outbound connection. The kernel refuses TCP (Landlock), UDP and raw sockets (seccomp, plus a dropped CAP_NET_RAW).
| Export | Input | Output | What it does |
|---|---|---|---|
write_file |
{ path, content } |
Writes a file under /workspace, then publishes fs.file_created |
|
read_file |
{ path } |
{ content } |
Reads a file under /workspace |
list_files |
{ files: string[] } |
Lists /workspace |
|
write_context_file |
{ path, content } |
Writes a file under /context |
|
read_context_file |
{ path } |
{ content } |
Reads a file under /context |
tag_context_file |
{ path, task, relatedApps: string[] } |
Tags a /context file so query_context can find it |
|
query_context |
{ text } |
{ results: [] } |
Searches /context by tag text and returns metadata for each match |
publish_context_event |
{ topic, payload } |
Publishes any event on the context bus |
Paths are relative to /workspace, or to /context for the context exports, and a file's folders are created as needed.
An absolute path is used as written, not joined onto /workspace. /workspace/notes.txt is the workspace's notes.txt, but /notes.txt is a file at the root of the filesystem, outside the workspace, and writing it fails. The error says where the path resolved. To mean the workspace, pass notes.txt or /workspace/notes.txt.
Anything outside the app's declared scope is refused by the kernel (Landlock), when the host can enforce it. Computer.boot() and production images set BERTH_REQUIRE_ENFORCEMENT=1, so there an app the kernel can't confine doesn't start. berth dev runs apps anyway on a host without Landlock, such as Docker Desktop, and the boot log says NOT RESTRICTED: nothing then stops a path outside the scope except the file's own permissions. See Kernel enforcement, by platform.
- Context bus. After every
write_file, the app publishesfs.file_createdwith{ path, createdBy: "filesystem" }.code-editorandactivity-feedsubscribe to it. See Talking to other apps. - Semantic FS. Write a file to
/context, tag it with a task and related apps, and any app in the sandbox can find it later by describing what it needs. Search ranks over the tag text, the path andcreated_by, never the file contents, so tag what you want found.query_contextreturns metadata only; callread_context_filefor each hit you need. See semantic FS.