On this page

The Filesystem API gives you full programmatic access to files and directories inside a running computer. All operations are proxied through the in-VM envd daemon and restricted to allowed paths.

Base path: /api/v1/computers/{id}/files

Allowed paths: /home/user, /home/ubuntu, /tmp


Quick Start

import { Miosa } from '@miosa/sdk';

const client = new Miosa();

// Write a file
await client.files.write(computerId, {
  path: '/home/user/hello.py',
  content: 'print("Hello from MIOSA")',
});

// List the directory
const { files } = await client.files.list(computerId, '/home/user');

// Download it back
const content = await client.files.download(computerId, '/home/user/hello.py');

Endpoints

MethodPathDescription
POST/files/uploadUpload a file (multipart)
POST/files/writeWrite text/binary content directly
GET/filesList directory (readdir)
GET/files/downloadDownload a file
POST/files/exportDownload with metadata (base64)
DELETE/filesDelete a file
GET/files/statStat a file or directory
POST/files/mkdirCreate a directory
POST/files/renameRename or move a file/directory
POST/files/copyCopy a file
POST/files/chmodChange file permissions

Upload a File

POST /api/v1/computers/{id}/files/upload

Multipart upload — use this for binary files or large payloads.

Request — multipart/form-data

FieldTypeRequiredDescription
filefileYesFile to upload
pathstringNoRemote path. If ends with /, filename is appended. Default: /home/user/

Response — 201 Created

{
  "success": true,
  "file": {
    "path": "/home/user/script.py",
    "filename": "script.py",
    "size_bytes": 1234
  }
}

Errors

StatusCodeDescription
400MISSING_FILENo file field in request
403FORBIDDEN_PATHPath outside allowed directories
413FILE_TOO_LARGEFile exceeds 10 MB limit
502AGENT_UNAVAILABLEIn-VM agent unreachable
curl -X POST https://api.miosa.ai/api/v1/computers/{id}/files/upload 
  -H "Authorization: Bearer $MIOSA_API_KEY" 
  -F "file=@./script.py" 
  -F "path=/home/user/scripts/"

Write a File

POST /api/v1/computers/{id}/files/write

Write text or base64-encoded binary content. Faster than multipart for programmatic use.

Request Body

FieldTypeRequiredDescription
pathstringYesFull destination path
contentstringYesFile content (UTF-8 text or base64 when encoding="base64")
encodingstringNo"utf8" (default) or "base64"

Response — 201 Created

{
  "success": true,
  "file": {
    "path": "/home/user/config.json",
    "size_bytes": 256
  }
}
curl -X POST https://api.miosa.ai/api/v1/computers/{id}/files/write 
  -H "Authorization: Bearer $MIOSA_API_KEY" 
  -H "Content-Type: application/json" 
  -d '{"path": "/home/user/config.json", "content": "{"key": "value"}"}'

List Directory (readdir)

GET /api/v1/computers/{id}/files

Query Parameters

ParameterTypeRequiredDescription
pathstringNoDirectory to list. Default: /home/user

Response — 200 OK

{
  "files": [
    {
      "name": "Documents",
      "type": "directory",
      "size_bytes": 4096,
      "permissions": "drwxr-xr-x",
      "modified_at": "2026-04-11T10:30:00Z"
    },
    {
      "name": "script.py",
      "type": "file",
      "size_bytes": 1234,
      "permissions": "-rw-r--r--",
      "modified_at": "2026-04-11T10:25:00Z"
    }
  ],
  "path": "/home/user"
}
curl "https://api.miosa.ai/api/v1/computers/{id}/files?path=/home/user" 
  -H "Authorization: Bearer $MIOSA_API_KEY"

Download a File

GET /api/v1/computers/{id}/files/download

Query Parameters

ParameterTypeRequiredDescription
pathstringYesFull path to the file

Response — 200 OK

Binary file content. Content-Disposition is set for browser download.

Errors

StatusCodeDescription
400MISSING_PATHNo path parameter
403FORBIDDEN_PATHPath outside allowed directories
404FILE_NOT_FOUNDFile does not exist
502AGENT_UNAVAILABLEIn-VM agent unreachable
curl "https://api.miosa.ai/api/v1/computers/{id}/files/download?path=/home/user/output.txt" 
  -H "Authorization: Bearer $MIOSA_API_KEY" 
  --output output.txt

Export a File

POST /api/v1/computers/{id}/files/export

Returns metadata and base64-encoded content in a single JSON response. Useful when you need the file and its metadata together without handling binary responses.

Request Body

FieldTypeRequiredDescription
pathstringYesFull path to the file

Response — 200 OK

{
  "success": true,
  "file": {
    "path": "/home/user/data.json",
    "filename": "data.json",
    "size_bytes": 5678,
    "content_type": "application/json",
    "modified_at": "2026-04-11T10:30:00Z"
  },
  "content_base64": "eyJrZXkiOiAidmFsdWUifQ=="
}
curl -X POST https://api.miosa.ai/api/v1/computers/{id}/files/export 
  -H "Authorization: Bearer $MIOSA_API_KEY" 
  -H "Content-Type: application/json" 
  -d '{"path": "/home/user/data.json"}'

Delete a File

DELETE /api/v1/computers/{id}/files

Request Body

FieldTypeRequiredDescription
pathstringYesFull path to delete

Response — 200 OK

{ "success": true, "path": "/home/user/old-file.txt" }
curl -X DELETE https://api.miosa.ai/api/v1/computers/{id}/files 
  -H "Authorization: Bearer $MIOSA_API_KEY" 
  -H "Content-Type: application/json" 
  -d '{"path": "/home/user/old-file.txt"}'

Stat a File or Directory

GET /api/v1/computers/{id}/files/stat

Returns metadata without downloading file content.

Query Parameters

ParameterTypeRequiredDescription
pathstringYesFull path

Response — 200 OK

{
  "path": "/home/user/script.py",
  "type": "file",
  "size_bytes": 1234,
  "permissions": "-rw-r--r--",
  "owner": "user",
  "group": "user",
  "modified_at": "2026-04-11T10:25:00Z",
  "created_at": "2026-04-11T09:00:00Z"
}

Errors

StatusCodeDescription
404FILE_NOT_FOUNDPath does not exist
curl "https://api.miosa.ai/api/v1/computers/{id}/files/stat?path=/home/user/script.py" 
  -H "Authorization: Bearer $MIOSA_API_KEY"

Create a Directory

POST /api/v1/computers/{id}/files/mkdir

Request Body

FieldTypeRequiredDescription
pathstringYesDirectory path to create
recursivebooleanNoCreate parent directories if missing (default: false)

Response — 201 Created

{ "success": true, "path": "/home/user/project/src" }
curl -X POST https://api.miosa.ai/api/v1/computers/{id}/files/mkdir 
  -H "Authorization: Bearer $MIOSA_API_KEY" 
  -H "Content-Type: application/json" 
  -d '{"path": "/home/user/project/src", "recursive": true}'

Rename or Move

POST /api/v1/computers/{id}/files/rename

Renames a file or directory, or moves it to a different path. Both source and destination must be within allowed paths.

Request Body

FieldTypeRequiredDescription
sourcestringYesCurrent path
destinationstringYesTarget path

Response — 200 OK

{ "success": true, "source": "/home/user/old.txt", "destination": "/home/user/new.txt" }

Errors

StatusCodeDescription
404FILE_NOT_FOUNDSource path does not exist
409DESTINATION_EXISTSDestination already exists
curl -X POST https://api.miosa.ai/api/v1/computers/{id}/files/rename 
  -H "Authorization: Bearer $MIOSA_API_KEY" 
  -H "Content-Type: application/json" 
  -d '{"source": "/home/user/app.py", "destination": "/home/user/app_v2.py"}'

Copy a File

POST /api/v1/computers/{id}/files/copy

Copies a file to a new path. Directory copy is not supported — copy individual files.

Request Body

FieldTypeRequiredDescription
sourcestringYesSource file path
destinationstringYesDestination file path

Response — 201 Created

{ "success": true, "source": "/home/user/template.py", "destination": "/home/user/project/main.py" }
curl -X POST https://api.miosa.ai/api/v1/computers/{id}/files/copy 
  -H "Authorization: Bearer $MIOSA_API_KEY" 
  -H "Content-Type: application/json" 
  -d '{"source": "/home/user/template.py", "destination": "/home/user/project/main.py"}'

Change Permissions

POST /api/v1/computers/{id}/files/chmod

Sets UNIX file permissions.

Request Body

FieldTypeRequiredDescription
pathstringYesFile path
modestringYesOctal permission string, e.g. "755" or "644"

Response — 200 OK

{ "success": true, "path": "/home/user/script.sh", "mode": "755" }
curl -X POST https://api.miosa.ai/api/v1/computers/{id}/files/chmod 
  -H "Authorization: Bearer $MIOSA_API_KEY" 
  -H "Content-Type: application/json" 
  -d '{"path": "/home/user/script.sh", "mode": "755"}'

Path Security

All paths are validated before execution:

  1. ExpansionPath.expand resolves .., ~, and symlinks
  2. Prefix check — Must start with /home/user, /home/ubuntu, or /tmp
  3. Rejection — Paths outside these prefixes return 403 FORBIDDEN_PATH

/home/user/../../etc/passwd expands to /etc/passwd and is rejected.


Common Recipes

Scaffold a project directory

// Create structure
await client.files.mkdir(computerId, { path: '/home/user/app/src', recursive: true });
await client.files.mkdir(computerId, { path: '/home/user/app/tests', recursive: true });

// Write files
await client.files.write(computerId, { path: '/home/user/app/src/main.py', content: mainPy });
await client.files.write(computerId, { path: '/home/user/app/requirements.txt', content: reqs });

// Make entrypoint executable
await client.files.chmod(computerId, { path: '/home/user/app/src/main.py', mode: '755' });

Retrieve build artifacts

# List the build output directory
files = client.files.list(computer_id, path="/home/user/app/dist")
for f in files["files"]:
    if f["type"] == "file":
        content = client.files.export(computer_id, path=f"/home/user/app/dist/{f['name']}")
        save_artifact(f["name"], content["content_base64"])

Was this helpful?