Files API
List Files
List files in a directory within the workspace.
curl 'https://api.nanoterm.dev/api/workspaces/ws_a1b2c3d4/files?path=/workspace' \
-H 'Authorization: Bearer nt_xxx'
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
path | string | /workspace | Directory to list |
Response: 200 OK
{
"files": [
{ "name": "src", "size": 4096, "isDirectory": true },
{ "name": "package.json", "size": 1234, "isDirectory": false },
{ "name": "README.md", "size": 567, "isDirectory": false }
]
}
Read File
Read the contents of a file from the workspace.
curl https://api.nanoterm.dev/api/workspaces/ws_a1b2c3d4/files/workspace/package.json \
-H 'Authorization: Bearer nt_xxx'
Response: 200 OK — raw file contents as binary
Write File
Write content to a file in the workspace.
curl -X PUT https://api.nanoterm.dev/api/workspaces/ws_a1b2c3d4/files/workspace/hello.txt \
-H 'Authorization: Bearer nt_xxx' \
-d 'Hello from NanoTerm!'
Request Body: raw file content
Response: 200 OK
{
"ok": true,
"bytes": 3712,
"mtime": "2026-05-06T08:01:23.000Z"
}
The server stats the file after writing and returns the actual byte
count + mtime. Callers should verify bytes matches the upload size;
the SDK's writeFile does this automatically and throws on mismatch.
Path constraints
All three endpoints (list, read, write) refuse paths that escape
/workspace. Symbolic-traversal attempts (/workspace/../etc),
sibling absolute paths (/etc/passwd, /proc/self/environ), and
embedded NUL bytes return 400 INVALID_PATH. Inputs are POSIX-
normalised first, so /workspace/./a/../b is canonicalised to
/workspace/b and accepted.
500 FILE_WRITE_BYTE_MISMATCH
The bytes on disk don't match what was received over the wire — usually a streaming / proxy-buffer issue. Retry; if it persists, file a bug.
{
"error": {
"code": "FILE_WRITE_BYTE_MISMATCH",
"message": "Wrote 3700 bytes to /workspace/foo but received 3712 bytes from client.",
"retryable": true
}
}